Merge branch '2.8' into 2.9
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index 0874104..8451799 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -1,6 +1,7 @@
 We appreciate issues as very valuable contributions, but just to make sure here are things that are important to do before filing an issue:
 
-* Only report issues, and ask Usage Questions on [Jackson-users](https://groups.google.com/forum/#!search/jackson-users) list -- ypu are more likely to get help that way
+* Only report issues (and perhaps request new features, FEATURE):,Usage Questions should be asked on [Jackson-users](https://groups.google.com/forum/#!search/jackson-users) list -- you are more likely to get help that way (and we will promptly close questions-as-issues)
 * Check to see if this issue has already been reported (quick glance at existing issues): no deep search necessary, just quick sanity check
 * Include version information for Jackson version you use
 * (optional but highly recommended) Verify that the problem occurs with the latest patch of same minor version; and even better, if possible, try using the latest stable patch version
+    * For example: if you observe an issue with version `2.4.1`, first upgrade to `2.4.6` to ensure problem has not already been fixed.
diff --git a/.gitignore b/.gitignore
index 1271c27..97155eb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
 # use glob syntax.
 syntax: glob
+
 *.class
 *~
 *.bak
diff --git a/.travis.yml b/.travis.yml
index 5a380fe..4b2bbb4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,13 +8,15 @@
 # Below this line is configuration for deploying to the Sonatype OSS repo
 # http://blog.xeiam.com/2013/05/configure-travis-ci-to-deploy-snapshots.html
 before_install: "git clone -b travis `git config --get remote.origin.url` target/travis"
-after_success: "mvn source:jar javadoc:jar deploy --settings target/travis/settings.xml"
+after_success:
+  - "mvn source:jar javadoc:jar deploy --settings target/travis/settings.xml"
+  - "mvn -B cobertura:cobertura coveralls:report"
 
 # whitelist
 branches:
   only:
     - master
-    - "2.8"
+    - "2.9"
 
 env:
   global:
diff --git a/README.md b/README.md
index 0bff2e5..7076dd2 100644
--- a/README.md
+++ b/README.md
@@ -25,7 +25,7 @@
 <properties>
   ...
   <!-- Use the latest version whenever possible. -->
-  <jackson.version>2.7.0</jackson.version>
+  <jackson.version>2.9.0</jackson.version>
   ...
 </properties>
 
diff --git a/pom.xml b/pom.xml
index 3a8edca..7153ea0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,13 +4,13 @@
 
   <parent>
     <groupId>com.fasterxml.jackson</groupId>
-    <artifactId>jackson-parent</artifactId>
-    <version>2.8</version>
+    <artifactId>jackson-base</artifactId>
+    <version>2.9.6</version>
   </parent>
 
   <groupId>com.fasterxml.jackson.core</groupId>
   <artifactId>jackson-databind</artifactId>
-  <version>2.8.11.3-SNAPSHOT</version>
+  <version>2.9.7-SNAPSHOT</version>
   <name>jackson-databind</name>
   <packaging>bundle</packaging>
   <description>General data-binding functionality for Jackson: works on core streaming API</description>
@@ -25,9 +25,11 @@
   </scm>
 
   <properties>
-    <!-- With Jackson 2.8 we will require JDK 7 (except for annotations/streaming),
+    <!-- With Jackson 2.9 we will require JDK 7 (except for annotations/streaming),
          and new language features (diamond pattern) may be used.
-         Whether JDK dependencies are directly used is an open question as of Feb-2016
+         JDK classes are still loaded dynamically since there isn't much downside
+         (small number of types); this allows use on JDK 6 platforms still (including
+         Android)
       -->
     <javac.src.version>1.7</javac.src.version>
     <javac.target.version>1.7</javac.target.version>
@@ -39,6 +41,9 @@
     <!-- Generate PackageVersion.java into this directory. -->
     <packageVersion.dir>com/fasterxml/jackson/databind/cfg</packageVersion.dir>
     <packageVersion.package>com.fasterxml.jackson.databind.cfg</packageVersion.package>
+
+    <!-- since 2.9.1: NOTE! can not use packageVersion.package as is -->
+    <jdk.module.name>com.fasterxml.jackson.databind</jdk.module.name>
   </properties>
 
   <dependencies>
@@ -46,11 +51,16 @@
     <dependency>
       <groupId>com.fasterxml.jackson.core</groupId>
       <artifactId>jackson-annotations</artifactId>
+      <!-- 06-Mar-2017, tatu: Although bom provides for dependencies, some legacy
+             usage seems to benefit from actually specifying version here in case
+             it is dependent on transitively
+        -->
+      <version>${jackson.version.annotations}</version>
     </dependency>
     <dependency>
       <groupId>com.fasterxml.jackson.core</groupId>
       <artifactId>jackson-core</artifactId>
-      <version>2.8.10</version>
+      <version>${jackson.version.core}</version>
     </dependency>
 
     <!-- and for testing we need a few libraries
@@ -58,20 +68,15 @@
       -->
 
     <dependency>
-      <groupId>junit</groupId>
-      <artifactId>junit</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
       <groupId>org.powermock</groupId>
       <artifactId>powermock-module-junit4</artifactId>
-      <version>1.6.5</version>
+      <version>1.7.3</version>
       <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>org.powermock</groupId>
       <artifactId>powermock-api-mockito</artifactId>
-      <version>1.6.5</version>
+      <version>1.7.3</version>
       <scope>test</scope>
     </dependency>
     <!-- For testing TestNoClassDefFoundDeserializer -->
@@ -81,16 +86,34 @@
       <version>1.0.0</version>
       <scope>test</scope>
     </dependency>
-    <dependency>
-      <groupId>com.google.jimfs</groupId>
-      <artifactId>jimfs</artifactId>
-      <version>1.1</version>
-      <scope>test</scope>
-    </dependency>
   </dependencies>
 
+  <!-- Alas, need to include snapshot reference since otherwise can not find
+       snapshot of parent... -->
+  <repositories>
+    <repository>
+      <id>sonatype-nexus-snapshots</id>
+      <name>Sonatype Nexus Snapshots</name>
+      <url>https://oss.sonatype.org/content/repositories/snapshots</url>
+      <releases><enabled>false</enabled></releases>
+      <snapshots><enabled>true</enabled></snapshots>
+    </repository>
+  </repositories>
+
   <build>
      <plugins>
+      <!-- Important: enable enforcer plug-in: -->
+      <plugin>
+        <artifactId>maven-enforcer-plugin</artifactId>
+        <executions> <!-- or?  combine.children="merge"> -->
+          <execution>
+            <id>enforce-properties</id>
+	    <phase>validate</phase>
+            <goals><goal>enforce</goal></goals>
+          </execution>
+        </executions>
+      </plugin>
+
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <version>${version.plugin.surefire}</version>
@@ -105,35 +128,29 @@
         </configuration>
       </plugin>
 
+      <!-- parent definitions should be ok, but need to add more links -->
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-javadoc-plugin</artifactId>
-        <version>${version.plugin.javadoc}</version>
         <configuration>
-<!-- Only works on Java 8:
-          <additionalparam>-Xdoclint:none</additionalparam>
--->
-<!-- so with Java 7, use this: -->
-          <failOnError>false</failOnError>
-          <links>
-            <link>http://docs.oracle.com/javase/7/docs/api/</link>
-            <link>http://fasterxml.github.com/jackson-annotations/javadoc/2.7</link>
-            <link>http://fasterxml.github.com/jackson-core/javadoc/2.7</link>
+          <links combine.children="append">
+            <link>http://fasterxml.github.com/jackson-annotations/javadoc/2.9</link>
+            <link>http://fasterxml.github.com/jackson-core/javadoc/2.9</link>
           </links>
         </configuration>
       </plugin>
 
-      <!-- May want to configure debug info -->
+      <!-- settings are fine, but needed to trigger execution! -->
       <plugin>
-        <!-- Inherited from oss-base. Generate PackageVersion.java.-->
         <groupId>com.google.code.maven-replacer-plugin</groupId>
         <artifactId>replacer</artifactId>
-        <executions>
-          <execution>
-            <id>process-packageVersion</id>
-            <phase>process-sources</phase>
-          </execution>
-        </executions>
+      </plugin>
+
+      <!-- 18-Oct-2016, tatu: Try to make coveralls work -->
+      <plugin>
+        <groupId>org.eluder.coveralls</groupId>
+        <artifactId>coveralls-maven-plugin</artifactId>
+        <version>4.3.0</version>
       </plugin>
     </plugins>
   </build>
diff --git a/release-notes/CREDITS b/release-notes/CREDITS-2.x
similarity index 78%
rename from release-notes/CREDITS
rename to release-notes/CREDITS-2.x
index fcfc2ba..eea9aa5 100644
--- a/release-notes/CREDITS
+++ b/release-notes/CREDITS-2.x
@@ -452,6 +452,8 @@
    (2.7.4)
   * Reported #1231: `@JsonSerialize(as=superType)` behavior disallowed in 2.7.4
    (2.7.5)
+  * Suggested #507: Support for default `@JsonView` for a class
+   (2.9.0)
 
 Tom Mack (tommack@github)
   * Reported #1208: treeToValue doesn't handle POJONodes that contain exactly
@@ -466,6 +468,8 @@
 Nick Babcock (nickbabcock)
   * Reported #1225: `JsonMappingException` should override getProcessor()
    (2.7.5)
+  * Suggested #1356: Differentiate between input and code exceptions on deserialization
+   (2.9.0)
 
 Andrew Joseph (apjoseph@github)
   * Reported #1248: `Annotated` returns raw type in place of Generic Type in 2.7.x
@@ -628,11 +632,22 @@
    (2.8.9)
   * Reported #1651: `StdDateFormat` fails to parse 'zulu' date when TimeZone other than UTC
    (2.8.9)
+  * Suggested #1745: StdDateFormat: accept and truncate millis larger than 3 digits
+   (2.9.1)
+  * Contributed #1749: StdDateFormat: performance improvement of '_format(..)' method
+   (2.9.1)
+  * Contributed #1759: Reuse `Calendar` instance during parsing by `StdDateFormat`
+   (2.9.1)
 
 Kevin Gallardo (newkek@github)
   * Reported #1658: Infinite recursion when deserializing a class extending a Map,
     with a recursive value type
    (2.8.10)
+  * Reported #1729: Integer bounds verification when calling `TokenBuffer.getIntValue()`
+   (2.9.4)
+
+Lukas Euler
+  * Reported #1735: Missing type checks when using polymorphic type ids
 
 Guixiong Wu (吴桂雄)
   * Reported #2032: Blacklist another serialization gadget (ibatis)
@@ -645,3 +660,149 @@
 Connor Kuhn (ckuhn@github)
   * Contributed #1341: FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY
    (2.9.0)
+
+Jan Lolling (jlolling@github)
+  * Contributed #1319: Add `ObjectNode.put(String, BigInteger)`
+   (2.9.0)
+
+Michael R Fairhurst (MichaelRFairhurst@github)
+  * Reported #1035: `@JsonAnySetter` assumes key of `String`, does not consider declared type.
+   (2.9.0)
+
+Fabrizio Cucci (fabriziocucci@github)
+  * Reported #1406: `ObjectMapper.readTree()` methods do not return `null` on end-of-input
+   (2.9.0)
+
+Emiliano Clariá (emilianogc@github)
+  * Contributed #1434: Explicitly pass null on invoke calls with no arguments
+   (2.9.0)
+
+Ana Eliza Barbosa (AnaEliza@github)
+  * Contributed #1520: Case insensitive enum deserialization feature.
+   (2.9.0)
+
+Lyor Goldstein (lgoldstein@github)
+  * Reported #1544: `EnumMapDeserializer` assumes a pure `EnumMap` and does not support
+    derived classes
+   (2.9.0)
+
+Harleen Sahni (harleensahni@github)
+  * Reported #403: Make FAIL_ON_NULL_FOR_PRIMITIVES apply to primitive arrays and other
+    types that wrap primitives
+   (2.9.0)
+
+Jared Jacobs (2is10@github)
+  * Requested #1605: Allow serialization of `InetAddress` as simple numeric host address
+   (2.9.0)
+
+Patrick Gunia (pgunia@github)
+  * Reported #1440: Wrong `JsonStreamContext` in `DeserializationProblemHandler` when reading
+  `TokenBuffer` content
+   (2.9.0)
+
+Carsten Wickner (CarstenWickner@github)
+  * Contributed #1522: Global `@JsonInclude(Include.NON_NULL)` for all properties with a specific type
+   (2.9.0)
+
+Chris Plummer (strmer15@github)
+  * Reported #1637: `ObjectReader.at()` with `JsonPointer` stops after first collection
+   (2.9.0)
+
+Christian Basler (Dissem@github)
+  * Reported #1688: Deserialization fails for `java.nio.file.Path` implementations when
+    default typing enabled
+   (2.9.0)
+
+Tim Bartley (tbartley@github)
+  * Reported, suggested fix for #1705: Non-generic interface method hides type resolution info
+    from generic base class
+   (2.9.1)
+
+Luís Cleto (luiscleto@github)
+  * Suggested 1768: Improve `TypeFactory.constructFromCanonical()` to work with
+   `java.lang.reflect.Type.getTypeName()` format
+   (2.9.2)
+
+Vincent Demay (vdemay@github)
+  * Reported #1793: `java.lang.NullPointerException` in `ObjectArraySerializer.acceptJsonFormatVisitor()`
+    for array value with `@JsonValue`
+   (2.9.2)
+
+Peter Jurkovic (peterjurkovic@github)
+  * Reported #1823: ClassNameIdResolver doesn't handle resolve Collections$SingletonMap,
+    Collections$SingletonSet
+   (2.9.3)
+
+alinakovalenko@github:
+  * Reported #1844: Map "deep" merge only adds new items, but not override existing values
+   (2.9.3)
+
+Pier-Luc Whissell (pwhissell@github):
+  * Reported #1673: Serialising generic value classes via Reference Types (like Optional) fails
+    to include type information
+   (2.9.4)
+
+Alexander Skvortcov (askvortcov@github)
+  * Reported #1853: Deserialise from Object (using Creator methods) returns field name
+    instead of value
+   (2.9.4)
+
+Joe Schafer (jschaf@github)
+  * Reported #1906: Add string format specifier for error message in `PropertyValueBuffer`
+   (2.9.4)
+  * Reported #1907: Remove `getClass()` from `_valueType` argument for error reporting
+   (2.9.4)
+
+Deblock Thomas (deblockt@github)
+  * Reported, contributed fix for #1912: `BeanDeserializerModifier.updateBuilder()` does not
+    work to set custom  deserializer on a property (since 2.9.0)
+ (contributed by Deblock T)
+
+lilei@venusgroup.com.cn:
+  * Reported #1931: Two more `c3p0` gadgets to exploit default typing issue
+   (2.9.5)
+
+Aniruddha Maru (maroux@github)
+  * Reported #1940: `Float` values with integer value beyond `int` lose precision if
+    bound to `long`
+   (2.9.5)
+
+Timur Shakurov (saladinkzn@github)
+  * Reported #1947: `MapperFeature.AUTO_DETECT_XXX` do not work if all disabled
+   (2.9.5)
+
+roeltje25@github
+  * Reported #1978: Using @JsonUnwrapped annotation in builderdeserializer hangs in
+    infinite loop
+   (2.9.5)
+
+Freddy Boucher (freddyboucher@github)
+  * Reported #1990: MixIn `@JsonProperty` for `Object.hashCode()` is ignored
+   (2.9.6)
+
+Ondrej Zizka (OndraZizk@github)
+  * Reported #1999: "Duplicate property" issue should mention which class it complains about
+   (2.9.6)
+
+Jakub Skierbiszewski (jskierbi@github)
+  * Reported, contributed fix for #2001: Deserialization issue with `@JsonIgnore` and
+    `@JsonCreator` + `@JsonProperty` for same property name
+   (2.9.6)
+
+Carter Kozak (cakofony@github)
+  * Reported #2016: Delegating JsonCreator disregards JsonDeserialize info
+   (2.9.6)
+
+Reinhard Prechtl (dnno@github)
+  * Reported #2034: Serialization problem with type specialization of nested generic types
+   (2.9.6)
+
+Chetan Narsude (243826@github)
+  * Reported #2038: JDK Serializing and using Deserialized `ObjectMapper` loses linkage
+    back from `JsonParser.getCodec()`
+  (2.9.6)
+
+Petar Tahchiev (ptahchiev@github)
+  * Reported #2060: `UnwrappingBeanPropertyWriter` incorrectly assumes the found
+    serializer is of type `UnwrappingBeanSerializer`
+  (2.9.6)
diff --git a/release-notes/VERSION b/release-notes/VERSION-2.x
similarity index 82%
rename from release-notes/VERSION
rename to release-notes/VERSION-2.x
index 2f72cd3..32c2069 100644
--- a/release-notes/VERSION
+++ b/release-notes/VERSION-2.x
@@ -1,13 +1,285 @@
 Project: jackson-databind
+
 ------------------------------------------------------------------------
-=== Releases ===
+=== Releases === 
 ------------------------------------------------------------------------
 
-2.8.11.3 (not yet released)
+2.9.7 (not yet released)
 
+#2060: `UnwrappingBeanPropertyWriter` incorrectly assumes the found serializer is
+  of type `UnwrappingBeanSerializer`
+ (reported by Petar T)
+#2082: `FactoryBasedEnumDeserializer` should be cachable
 #2109: Canonical string for reference type is built incorrectly
  (reported by svarzee@github)
 
+2.9.6 (12-Jun-2018)
+
+#955: Add `MapperFeature.USE_BASE_TYPE_AS_DEFAULT_IMPL` to use declared base type
+   as `defaultImpl` for polymorphic deserialization
+  (contributed by mikeldpl@github)
+#1328: External property polymorphic deserialization does not work with enums
+#1565: Deserialization failure with Polymorphism using JsonTypeInfo `defaultImpl`,
+  subtype as target
+#1964: Failed to specialize `Map` type during serialization where key type
+  incompatibility overidden via "raw" types
+ (reported by ptirador@github)
+#1990: MixIn `@JsonProperty` for `Object.hashCode()` is ignored
+ (reported by Freddy B)
+#1991: Context attributes are not passed/available to custom serializer if object is in POJO
+ (reported by dletin@github)
+#1998: Removing "type" attribute with Mixin not taken in account if
+  using ObjectMapper.copy()
+ (reported by SBKila@github)
+#1999: "Duplicate property" issue should mention which class it complains about
+ (reported by Ondrej Z)
+#2001: Deserialization issue with `@JsonIgnore` and `@JsonCreator` + `@JsonProperty`
+  for same property name
+ (reported, fix contributed by Jakub S)
+#2015: `@Jsonsetter with Nulls.SKIP` collides with
+  `DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL` when parsing enum
+ (reported by ndori@github)
+#2016: Delegating JsonCreator disregards JsonDeserialize info
+ (reported by Carter K)
+#2019: Abstract Type mapping in 2.9 fails when multiple modules are registered
+ (reported by asger82@github)
+#2021: Delegating JsonCreator disregards `JsonDeserialize.using` annotation
+#2023: `JsonFormat.Feature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT` not working
+  with `null` coercion with `@JsonSetter`
+#2027: Concurrency error causes `IllegalStateException` on `BeanPropertyMap`
+ (reported by franboragina@github)
+#2032: CVE-2018-11307: Potential information exfiltration with default typing, serialization gadget from MyBatis
+ (reported by Guixiong Wu)
+#2034: Serialization problem with type specialization of nested generic types
+ (reported by Reinhard P)
+#2038: JDK Serializing and using Deserialized `ObjectMapper` loses linkage
+  back from `JsonParser.getCodec()`
+ (reported by Chetan N)
+#2051: Implicit constructor property names are not renamed properly with
+  `PropertyNamingStrategy`
+#2052: CVE-2018-12022: Block polymorphic deserialization of types from Jodd-db library
+ (reported by Guixiong Wu)
+#2058: CVE-2018-12023: Block polymorphic deserialization of types from Oracle JDBC driver
+ (reported by Guixiong Wu)
+
+2.9.5 (26-Mar-2018)
+
+#1911: Allow serialization of `BigDecimal` as String, using
+  `@JsonFormat(shape=Shape.String)`, config overrides
+ (suggested by cen1@github)
+#1912: `BeanDeserializerModifier.updateBuilder()` not work to set custom
+  deserializer on a property (since 2.9.0)
+ (contributed by Deblock T)
+#1931: Two more `c3p0` gadgets to exploit default typing issue
+ (reported by lilei@venusgroup.com.cn)
+#1932: `EnumMap` cannot deserialize with type inclusion as property
+#1940: `Float` values with integer value beyond `int` lose precision if
+  bound to `long`
+ (reported by Aniruddha M)
+#1941: `TypeFactory.constructFromCanonical()` throws NPE for Unparameterized
+  generic canonical strings
+ (reported by ayushgp@github)
+#1947: `MapperFeature.AUTO_DETECT_XXX` do not work if all disabled
+ (reported by Timur S)
+#1977: Serializing an Iterator with multiple sub-types fails after upgrading to 2.9.x
+ (reported by ssivanand@github)
+#1978: Using @JsonUnwrapped annotation in builderdeserializer hangs in infinite loop
+ (reported by roeltje25@github)
+
+2.9.4 (24-Jan-2018)
+
+#1382: `@JsonProperty(access=READ_ONLY)` unxepected behaviour with `Collections`
+ (reported by hexfaker@github)
+#1673: Serialising generic value classes via Reference Types (like Optional) fails
+  to include type information
+ (reported by Pier-Luc W)
+#1729: Integer bounds verification when calling `TokenBuffer.getIntValue()`
+ (reported by Kevin G)
+#1853: Deserialise from Object (using Creator methods) returns field name instead of value
+ (reported by Alexander S)
+#1854: NPE deserializing collection with `@JsonCreator` and `ACCEPT_CASE_INSENSITIVE_PROPERTIES`
+ (reported by rue-jw@github)
+#1855: Blacklist for more serialization gadgets (dbcp/tomcat, spring)
+#1859: Issue handling unknown/unmapped Enum keys
+ (reported by remya11@github)
+#1868: Class name handling for JDK unmodifiable Collection types changed
+  (reported by Rob W)
+#1870: Remove `final` on inherited methods in `BuilderBasedDeserializer` to allow
+  overriding by subclasses
+  (requested by Ville K)
+#1878: `@JsonBackReference` property is always ignored when deserializing since 2.9.0
+ (reported by reda-alaoui@github)
+#1895: Per-type config override "JsonFormat.Shape.OBJECT" for Map.Entry not working
+ (reported by mcortella@github)
+#1899: Another two gadgets to exploit default typing issue in jackson-databind
+ (reported by OneSourceCat@github)
+#1906: Add string format specifier for error message in `PropertyValueBuffer`
+ (reported by Joe S)
+#1907: Remove `getClass()` from `_valueType` argument for error reporting
+ (reported by Joe S)
+
+2.9.3 (09-Dec-2017)
+
+#1604: Nested type arguments doesn't work with polymorphic types
+#1794: `StackTraceElementDeserializer` not working if field visibility changed
+ (reported by dsingley@github)
+#1799: Allow creation of custom sub-types of `NullNode`, `BooleanNode`, `MissingNode`
+#1804: `ValueInstantiator.canInstantiate()` ignores `canCreateUsingArrayDelegate()`
+ (reported byb henryptung@github)
+#1807: Jackson-databind caches plain map deserializer and use it even map has `@JsonDeserializer`
+ (reported by lexas2509@github)
+#1823: ClassNameIdResolver doesn't handle resolve Collections$SingletonMap & Collections$SingletonSet
+ (reported by Peter J)
+#1831: `ObjectReader.readValue(JsonNode)` does not work correctly with polymorphic types,
+  value to update
+ (reported by basmastr@github)
+#1835: ValueInjector break from 2.8.x to 2.9.x
+ (repoted by kinigitbyday@github)
+#1842: `null` String for `Exception`s deserialized as String "null" instead of `null`
+ (reported by ZeleniJure@github)
+#1843: Include name of unsettable property in exception from `SetterlessProperty.set()`
+ (suggested by andreh7@github)
+#1844: Map "deep" merge only adds new items, but not override existing values
+ (reported by alinakovalenko@github)
+
+2.9.2 (14-Oct-2017)
+
+(possibly) #1756: Deserialization error with custom `AnnotationIntrospector`
+ (reported by Daniel N)
+#1705: Non-generic interface method hides type resolution info from generic base class
+  (reported by Tim B)
+ NOTE: was originally reported fixed in 2.9.1 -- turns out it wasn't.
+#1767: Allow `DeserializationProblemHandler` to respond to primitive types
+ (reported by nhtzr@github)
+#1768: Improve `TypeFactory.constructFromCanonical()` to work with
+  `java.lang.reflect.Type.getTypeName()' format
+ (suggested by Luís C)
+#1771: Pass missing argument for string formatting in `ObjectMapper`
+ (reported by Nils B)
+#1788: `StdDateFormat._parseAsISO8601()` does not parse "fractional" timezone correctly
+#1793: `java.lang.NullPointerException` in `ObjectArraySerializer.acceptJsonFormatVisitor()`
+  for array value with `@JsonValue`
+ (reported by Vincent D)
+
+2.9.1 (07-Sep-2017)
+
+#1725: `NPE` In `TypeFactory. constructParametricType(...)`
+ (reported by ctytgat@github)
+#1730: InvalidFormatException` for `JsonToken.VALUE_EMBEDDED_OBJECT`
+ (reported by zigzago@github)
+#1744: StdDateFormat: add option to serialize timezone offset with a colon
+ (contributed by Bertrand R)
+#1745: StdDateFormat: accept and truncate millis larger than 3 digits
+ (suggested by Bertrand R)
+#1749: StdDateFormat: performance improvement of '_format(..)' method 
+ (contributed by Bertrand R)
+#1759: Reuse `Calendar` instance during parsing by `StdDateFormat`
+ (contributed by Bertrand R)
+- Fix `DelegatingDeserializer` constructor to pass `handledType()` (and
+  not type of deserializer being delegated to!)
+- Add `Automatic-Module-Name` ("com.fasterxml.jackson.databind") for JDK 9 module system
+
+2.9.0 (30-Jul-2017)
+
+#219: SqlDateSerializer does not obey SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS
+ (reported by BrentDouglas@github)
+#265: Add descriptive exception for attempts to use `@JsonWrapped` via Creator parameter
+#291: @JsonTypeInfo with As.EXTERNAL_PROPERTY doesn't work if external type property
+  is referenced more than once
+ (reported by Starkom@github)
+#357: StackOverflowError with contentConverter that returns array type
+ (reported by Florian S)
+#383: Recursive `@JsonUnwrapped` (`child` with same type) fail: "No _valueDeserializer assigned"
+ (reported by tdavis@github)
+#403: Make FAIL_ON_NULL_FOR_PRIMITIVES apply to primitive arrays and other types that wrap primitives
+ (reported by Harleen S)
+#476: Allow "Serialize as POJO" using `@JsonFormat(shape=Shape.OBJECT)` class annotation
+#507: Support for default `@JsonView` for a class
+ (suggested by Mark W)
+#687: Exception deserializing a collection @JsonIdentityInfo and a property based creator
+#865: `JsonFormat.Shape.OBJECT` ignored when class implements `Map.Entry`
+#888: Allow specifying custom exclusion comparator via `@JsonInclude`,
+  using `JsonInclude.Include.CUSTOM`
+#994: `DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS` only works for POJOs, Maps
+#1029: Add a way to define property name aliases
+#1035: `@JsonAnySetter` assumes key of `String`, does not consider declared type.
+ (reported by Michael F)
+#1060: Allow use of `@JsonIgnoreProperties` for POJO-valued arrays, `Collection`s
+#1106: Add `MapperFeature.ALLOW_COERCION_OF_SCALARS` for enabling/disabling coercions
+#1284: Make `StdKeySerializers` use new `JsonGenerator.writeFieldId()` for `int`/`long` keys
+#1320: Add `ObjectNode.put(String, BigInteger)`
+ (proposed by Jan L)
+#1341: `DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY`
+ (contributed by Connor K)
+#1347: Extend `ObjectMapper.configOverrides()` to allow changing visibility rules
+#1356: Differentiate between input and code exceptions on deserialization
+ (suggested by Nick B)
+#1369: Improve `@JsonCreator` detection via `AnnotationIntrospector`
+ by passing `MappingConfig`
+#1371: Add `MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES` to allow
+ disabling use of `@CreatorProperties` as explicit `@JsonCreator` equivalent
+#1376: Add ability to disable JsonAnySetter/JsonAnyGetter via mixin
+ (suggested by brentryan@github)
+#1399: Add support for `@JsonMerge` to allow "deep update"
+#1402: Use `@JsonSetter(nulls=...)` to specify handling of `null` values during deserialization
+#1406: `ObjectMapper.readTree()` methods do not return `null` on end-of-input
+ (reported by Fabrizio C)
+#1407: `@JsonFormat.pattern` is ignored for `java.sql.Date` valued properties
+ (reported by sangpire@github)
+#1415: Creating CollectionType for non generic collection class broken
+#1428: Allow `@JsonValue` on a field, not just getter
+#1434: Explicitly pass null on invoke calls with no arguments
+ (contributed by Emiliano C)
+#1433: `ObjectMapper.convertValue()` with null does not consider null conversions
+  (`JsonDeserializer.getNullValue()`)
+ (contributed by jdmichal@github)
+#1440: Wrong `JsonStreamContext` in `DeserializationProblemHandler` when reading
+  `TokenBuffer` content
+ (reported by Patrick G)
+#1444: Change `ObjectMapper.setSerializationInclusion()` to apply to content inclusion too
+#1450: `SimpleModule.addKeyDeserializer()' should throw `IllegalArgumentException` if `null`
+  reference of `KeyDeserializer` passed
+ (suggested by PawelJagus@github)
+#1454: Support `@JsonFormat.lenient` for `java.util.Date`, `java.util.Calendar`
+#1474: Replace use of `Class.newInstance()` (deprecated in Java 9) with call via Constructor
+#1480: Add support for serializing `boolean`/`Boolean` as number (0 or 1)
+ (suggested by jwilmoth@github)
+#1520: Case insensitive enum deserialization with `MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS`
+ (contributed by Ana-Eliza B)
+#1522: Global `@JsonInclude(Include.NON_NULL)` for all properties with a specific type
+ (contributed by Carsten W)
+#1544: EnumMapDeserializer assumes a pure EnumMap and does not support EnumMap derived classes
+ (reported by Lyor G)
+#1550: Unexpected behavior with `@JsonInclude(JsonInclude.Include.NON_EMPTY)` and
+ `java.util.Date` serialization
+#1551: `JsonMappingException` with polymorphic type and `JsonIdentityInfo` when basic type is abstract
+ (reported by acm073@github)
+#1552: Map key converted to byte array is not serialized as base64 string
+ (reported by nmatt@github)
+#1554: Support deserialization of `Shape.OBJECT` ("as POJO") for `Map`s (and map-like types)
+#1556: Add `ObjectMapper.updateValue()` method to update instance with given overrides
+ (suggested by syncer@github)
+#1583: Add a `DeserializationFeature.FAIL_ON_TRAILING_TOKENS` to force reading of the
+  whole input as single value
+#1592: Add support for handling primitive/discrepancy problem with type refinements
+#1605: Allow serialization of `InetAddress` as simple numeric host address
+ (requested by Jared J)
+#1616: Extraneous type id mapping added for base type itself
+#1619: By-pass annotation introspection for array types
+#1637: `ObjectReader.at()` with `JsonPointer` stops after first collection
+ (reported by Chris P)
+#1653: Convenience overload(s) for ObjectMapper#registerSubtypes
+#1655: `@JsonAnyGetter` uses different `bean` parameter in `SimpleBeanPropertyFilter`
+ (reported by georgeflugq@github)
+#1678: Rewrite `StdDateFormat` ISO-8601 handling functionality
+#1684: Rewrite handling of type ids to let `JsonGenerator` handle (more of) details
+#1688: Deserialization fails for `java.nio.file.Path` implementations when default typing
+  enabled
+ (reported by Christian B)
+#1690: Prevent use of quoted number (index) for Enum deserialization via
+  `MapperFeature.ALLOW_COERCION_OF_SCALARS`
+ (requested by magdel@github)
+
 2.8.11.2 (08-Jun-2018)
 
 #1941: `TypeFactory.constructFromCanonical()` throws NPE for Unparameterized
@@ -48,6 +320,7 @@
 #1657: `StdDateFormat` deserializes dates with no tz/offset as UTC instead of
   configured timezone
  (reported by Bertrand R)
+#1680: Blacklist couple more types for deserialization
 #1658: Infinite recursion when deserializing a class extending a Map,
   with a recursive value type
  (reported by Kevin G)
@@ -55,6 +328,7 @@
 #1711: Delegating creator fails to work for binary data (`byte[]`) with
  binary formats (CBOR, Smile)
 #1735: Missing type checks when using polymorphic type ids
+ (reported by Lukas Euler)
 #1737: Block more JDK types from polymorphic deserialization
 
 2.8.9 (12-Jun-2017)
@@ -165,7 +439,6 @@
 
 2.8.3 (17-Sep-2016)
 
-#929: `@JsonCreator` not working on a factory with multiple arguments for a enum type
 #1351: `@JsonInclude(NON_DEFAULT)` doesn't omit null fields
  (reported by Gili T)
 #1353: Improve error-handling for `java.net.URL` deserialization
diff --git a/src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java b/src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java
index c1e3655..bd83f0e 100644
--- a/src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java
+++ b/src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java
@@ -3,13 +3,10 @@
 import java.lang.annotation.Annotation;
 import java.util.*;
 
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonFormat;
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.*;
 import com.fasterxml.jackson.core.Version;
 import com.fasterxml.jackson.core.Versioned;
+
 import com.fasterxml.jackson.databind.JsonDeserializer;
 import com.fasterxml.jackson.databind.JsonSerializer;
 import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
@@ -20,8 +17,6 @@
 import com.fasterxml.jackson.databind.jsontype.NamedType;
 import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
 import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
-import com.fasterxml.jackson.databind.type.MapLikeType;
-import com.fasterxml.jackson.databind.type.TypeFactory;
 import com.fasterxml.jackson.databind.util.Converter;
 import com.fasterxml.jackson.databind.util.NameTransformer;
 
@@ -67,7 +62,7 @@
              * {@link com.fasterxml.jackson.annotation.JsonManagedReference}
              */
             MANAGED_REFERENCE
-    
+
             /**
              * Reference property that Jackson manages by suppressing it during serialization,
              * and reconstructing during deserialization.
@@ -238,56 +233,12 @@
      */
     public JsonIgnoreProperties.Value findPropertyIgnorals(Annotated ac)
     {
-        // 28-Apr-2016, tatu: For backwards compatibility let's delegate to older
-        //   methods, for Jackson 2.8
-        String[] ignorals = findPropertiesToIgnore(ac, true);
-        Boolean b = (ac instanceof AnnotatedClass) ?
-                findIgnoreUnknownProperties((AnnotatedClass) ac) : null;
-        JsonIgnoreProperties.Value v;
-        if (ignorals == null) {
-            if (b == null) {
-                return null;
-            }
-            v = JsonIgnoreProperties.Value.empty();
-        } else {
-            v = JsonIgnoreProperties.Value.forIgnoredProperties(ignorals);
-        }
-        if (b != null) {
-            v = b.booleanValue() ? v.withIgnoreUnknown() : v.withoutIgnoreUnknown();
-        }
-        return v;
+        // 18-Oct-2016, tatu: Used to call deprecated methods for backwards
+        //   compatibility in 2.8, but not any more in 2.9
+        return JsonIgnoreProperties.Value.empty();
     }
 
     /**
-     * @param forSerialization True if requesting properties to ignore for serialization;
-     *   false if for deserialization
-     * 
-     * @since 2.6
-     *
-     * @deprecated Since 2.8, use {@link #findPropertyIgnorals} instead
-     */
-    @Deprecated // since 2.8
-    public String[] findPropertiesToIgnore(Annotated ac, boolean forSerialization) {
-        return null;
-    }
-
-    /**
-     * @deprecated Since 2.6, use variant that takes second argument.
-     */
-    @Deprecated // since 2.6
-    public String[] findPropertiesToIgnore(Annotated ac) {
-        return null;
-    }
-
-    /**
-     * Method for checking whether an annotation indicates that all unknown properties
-     *
-     * @deprecated Since 2.8, use {@link #findPropertyIgnorals} instead
-     */
-    @Deprecated // since 2.8
-    public Boolean findIgnoreUnknownProperties(AnnotatedClass ac) { return null; }
-
-    /**
      * Method for checking whether properties that have specified type
      * (class, not generics aware) should be completely ignored for
      * serialization and deserialization purposes.
@@ -335,6 +286,35 @@
      */
     public String findClassDescription(AnnotatedClass ac) { return null; }
 
+    /**
+     * @param forSerialization True if requesting properties to ignore for serialization;
+     *   false if for deserialization
+     * 
+     * @since 2.6
+     *
+     * @deprecated Since 2.8, use {@link #findPropertyIgnorals} instead
+     */
+    @Deprecated // since 2.8
+    public String[] findPropertiesToIgnore(Annotated ac, boolean forSerialization) {
+        return null;
+    }
+
+    /**
+     * @deprecated Since 2.6, use variant that takes second argument.
+     */
+    @Deprecated // since 2.6
+    public String[] findPropertiesToIgnore(Annotated ac) {
+        return null;
+    }
+
+    /**
+     * Method for checking whether an annotation indicates that all unknown properties
+     *
+     * @deprecated Since 2.8, use {@link #findPropertyIgnorals} instead
+     */
+    @Deprecated // since 2.8
+    public Boolean findIgnoreUnknownProperties(AnnotatedClass ac) { return null; }
+    
     /*
     /**********************************************************
     /* Property auto-detection
@@ -365,7 +345,7 @@
      * instantiating resolver builder, but also configuring it based on
      * relevant annotations (not including ones checked with a call to
      * {@link #findSubtypes}
-     * 
+     *
      * @param config Configuration settings in effect (for serialization or deserialization)
      * @param ac Annotated class to check for annotations
      * @param baseType Base java type of value for which resolver is to be found
@@ -487,18 +467,26 @@
      * 
      * @return Identifier of value to inject, if any; null if no injection
      *   indicator is found
+     *
+     * @since 2.9
      */
-    public Object findInjectableValueId(AnnotatedMember m) { return null; }
+    public JacksonInject.Value findInjectableValue(AnnotatedMember m) {
+        // 05-Apr-2017, tatu: Just for 2.9, call deprecated method to help
+        //    with some cases of overrides for legacy code
+        Object id = findInjectableValueId(m);
+        if (id != null) {
+            return JacksonInject.Value.forId(id);
+        }
+        return null;
+    }
 
     /**
      * Method that can be called to check whether this member has
      * an annotation that suggests whether value for matching property
      * is required or not.
-     * 
-     * @since 2.0
      */
     public Boolean hasRequiredMarker(AnnotatedMember m) { return null; }
-    
+
     /**
      * Method for checking if annotated property (represented by a field or
      * getter/setter method) has definitions for views it is to be included in.
@@ -507,6 +495,9 @@
      * otherwise it will only be included for views included in returned
      * array. View matches are checked using class inheritance rules (sub-classes
      * inherit inclusions of super-classes)
+     *<p>
+     * Since 2.9 this method may also be called to find "default view(s)" for
+     * {@link AnnotatedClass}
      * 
      * @param a Annotated property (represented by a method, field or ctor parameter)
      * @return Array of views (represented by classes) that the property is included in;
@@ -522,7 +513,9 @@
      * 
      * @since 2.1
      */
-    public JsonFormat.Value findFormat(Annotated memberOrClass) { return null; }
+    public JsonFormat.Value findFormat(Annotated memberOrClass) {
+        return JsonFormat.Value.empty();
+    }
 
     /**
      * Method used to check if specified property has annotation that indicates
@@ -579,7 +572,7 @@
      * it is "weak" and does not either proof that a property exists (for example,
      * if visibility is not high enough), or override explicit names.
      * In practice this method is used to introspect optional names for creator
-     * parameters (which may or may not be available and can not be detected
+     * parameters (which may or may not be available and cannot be detected
      * by standard databind); or to provide alternate name mangling for
      * fields, getters and/or setters.
      * 
@@ -588,6 +581,16 @@
     public String findImplicitPropertyName(AnnotatedMember member) { return null; }
 
     /**
+     * Method called to find if given property has alias(es) defined.
+     * 
+     * @return `null` if member has no information; otherwise a `List` (possibly
+     *   empty) of aliases to use.
+     *
+     * @since 2.9
+     */
+    public List<PropertyName> findPropertyAliases(Annotated ann) { return null; }
+
+    /**
      * Method for finding optional access definition for a property, annotated
      * on one of its accessors. If a definition for read-only, write-only
      * or read-write cases, visibility rules may be modified. Note, however,
@@ -602,7 +605,7 @@
      * Method called in cases where a class has two methods eligible to be used
      * for the same logical property, and default logic is not enough to figure
      * out clear precedence. Introspector may try to choose one to use; or, if
-     * unable, return `null` to indicate it can not resolve the problem.
+     * unable, return `null` to indicate it cannot resolve the problem.
      *
      * @since 2.7
      */
@@ -611,6 +614,14 @@
         return null;
     }
 
+    /**
+     * @deprecated Since 2.9 Use {@link #findInjectableValue} instead
+     */
+    @Deprecated // since 2.9
+    public Object findInjectableValueId(AnnotatedMember m) {
+        return null;
+    }
+
     /*
     /**********************************************************
     /* Serialization: general annotations
@@ -721,6 +732,18 @@
     }
 
     /**
+     * Method for checking inclusion criteria for a type (Class) or property (yes, method
+     * name is bit unfortunate -- not just for properties!).
+     * In case of class, acts as the default for properties POJO contains; for properties
+     * acts as override for class defaults and possible global defaults.
+     *
+     * @since 2.6
+     */
+    public JsonInclude.Value findPropertyInclusion(Annotated a) {
+        return JsonInclude.Value.empty();
+    }
+
+    /**
      * Method for checking whether given annotated entity (class, method,
      * field) defines which Bean/Map properties are to be included in
      * serialization.
@@ -746,9 +769,9 @@
      * Method for checking whether content (entries) of a {@link java.util.Map} property
      * are to be included during serialization or not.
      * NOTE: this is NOT called for POJO properties, or array/Collection elements.
-     * 
+     *
      * @since 2.5
-     * 
+     *
      * @deprecated Since 2.7 Use {@link #findPropertyInclusion} instead
      */
     @Deprecated // since 2.7
@@ -756,18 +779,6 @@
         return defValue;
     }
 
-    /**
-     * Method for checking inclusion criteria for a type (Class) or property (yes, method
-     * name is bit unfortunate -- not just for properties!).
-     * In case of class, acts as the default for properties POJO contains; for properties
-     * acts as override for class defaults and possible global defaults.
-     *
-     * @since 2.6
-     */
-    public JsonInclude.Value findPropertyInclusion(Annotated a) {
-        return JsonInclude.Value.empty();
-    }
-
     /*
     /**********************************************************
     /* Serialization: type refinements
@@ -775,14 +786,19 @@
      */
 
     /**
-     * Method for accessing annotated type definition that a
-     * method/field can have, to be used as the type for serialization
-     * instead of the runtime type.
-     * Type returned (if any) needs to be widening conversion (super-type).
-     * Declared return type of the method is also considered acceptable.
+     * Method called to find out possible type refinements to use
+     * for deserialization, including not just value itself but
+     * key and/or content type, if type has those.
      *
-     * @return Class to use instead of runtime type
-     *
+     * @since 2.7
+     */
+    public JavaType refineSerializationType(final MapperConfig<?> config,
+            final Annotated a, final JavaType baseType) throws JsonMappingException
+    {
+        return baseType;
+    }
+
+    /**
      * @deprecated Since 2.7 call {@link #refineSerializationType} instead
      */
     @Deprecated // since 2.7
@@ -791,13 +807,6 @@
     }
 
     /**
-     * Method for finding possible widening type definition that a property
-     * value can have, to define less specific key type to use for serialization.
-     * It should be only be used with {@link java.util.Map} types.
-     * 
-     * @return Class specifying more general type to use instead of
-     *   declared type, if annotation found; null if not
-     *
      * @deprecated Since 2.7 call {@link #refineSerializationType} instead
      */
     @Deprecated // since 2.7
@@ -806,138 +815,13 @@
     }
 
     /**
-     * Method for finding possible widening type definition that a property
-     * value can have, to define less specific key type to use for serialization.
-     * It should be only used with structured types (arrays, collections, maps).
-     * 
-     * @return Class specifying more general type to use instead of
-     *   declared type, if annotation found; null if not
-     *
      * @deprecated Since 2.7 call {@link #refineSerializationType} instead
      */
     @Deprecated // since 2.7
     public Class<?> findSerializationContentType(Annotated am, JavaType baseType) {
         return null;
     }
-
-    /**
-     * Method called to find out possible type refinements to use
-     * for deserialization.
-     *
-     * @since 2.7
-     */
-    public JavaType refineSerializationType(final MapperConfig<?> config,
-            final Annotated a, final JavaType baseType) throws JsonMappingException
-    {
-        JavaType type = baseType;
-        final TypeFactory tf = config.getTypeFactory();
-        
-        // 10-Oct-2015, tatu: For 2.7, we'll need to delegate back to
-        //    now-deprecated secondary methods; this because while
-        //    direct sub-class not yet retrofitted may only override
-        //    those methods. With 2.8 or later we may consider removal
-        //    of these methods
-
-        
-        // Ok: start by refining the main type itself; common to all types
-        Class<?> serClass = findSerializationType(a);
-        if (serClass != null) {
-            if (type.hasRawClass(serClass)) {
-                // 30-Nov-2015, tatu: As per [databind#1023], need to allow forcing of
-                //    static typing this way
-                type = type.withStaticTyping();
-            } else {
-                Class<?> currRaw = type.getRawClass();
-                try {
-                    // 11-Oct-2015, tatu: For deser, we call `TypeFactory.constructSpecializedType()`,
-                    //   may be needed here too in future?
-                    if (serClass.isAssignableFrom(currRaw)) { // common case
-                        type = tf.constructGeneralizedType(type, serClass);
-                    } else if (currRaw.isAssignableFrom(serClass)) { // specialization, ok as well
-                        type = tf.constructSpecializedType(type, serClass);
-                    } else {
-                        throw new JsonMappingException(null,
-                                String.format("Can not refine serialization type %s into %s; types not related",
-                                        type, serClass.getName()));
-                    }
-                } catch (IllegalArgumentException iae) {
-                    throw new JsonMappingException(null,
-                            String.format("Failed to widen type %s with annotation (value %s), from '%s': %s",
-                                    type, serClass.getName(), a.getName(), iae.getMessage()),
-                                    iae);
-                }
-            }
-        }
-        // Then further processing for container types
-
-        // First, key type (for Maps, Map-like types):
-        if (type.isMapLikeType()) {
-            JavaType keyType = type.getKeyType();
-            Class<?> keyClass = findSerializationKeyType(a, keyType);
-            if (keyClass != null) {
-                if (keyType.hasRawClass(keyClass)) {
-                    keyType = keyType.withStaticTyping();
-                } else {
-                    Class<?> currRaw = keyType.getRawClass();
-                    try {
-                        // 19-May-2016, tatu: As per [databind#1231], [databind#1178] may need to actually
-                        //   specialize (narrow) type sometimes, even if more commonly opposite
-                        //   is needed.
-                        if (keyClass.isAssignableFrom(currRaw)) { // common case
-                            keyType = tf.constructGeneralizedType(keyType, keyClass);
-                        } else if (currRaw.isAssignableFrom(keyClass)) { // specialization, ok as well
-                            keyType = tf.constructSpecializedType(keyType, keyClass);
-                        } else {
-                            throw new JsonMappingException(null,
-                                    String.format("Can not refine serialization key type %s into %s; types not related",
-                                            keyType, keyClass.getName()));
-                        }
-                    } catch (IllegalArgumentException iae) {
-                        throw new JsonMappingException(null,
-                                String.format("Failed to widen key type of %s with concrete-type annotation (value %s), from '%s': %s",
-                                        type, keyClass.getName(), a.getName(), iae.getMessage()),
-                                        iae);
-                    }
-                }
-                type = ((MapLikeType) type).withKeyType(keyType);
-            }
-        }
-
-        JavaType contentType = type.getContentType();
-        if (contentType != null) { // collection[like], map[like], array, reference
-            // And then value types for all containers:
-           Class<?> contentClass = findSerializationContentType(a, contentType);
-           if (contentClass != null) {
-               if (contentType.hasRawClass(contentClass)) {
-                   contentType = contentType.withStaticTyping();
-               } else {
-                   // 03-Apr-2016, tatu: As per [databind#1178], may need to actually
-                   //   specialize (narrow) type sometimes, even if more commonly opposite
-                   //   is needed.
-                   Class<?> currRaw = contentType.getRawClass();
-                   try {
-                       if (contentClass.isAssignableFrom(currRaw)) { // common case
-                           contentType = tf.constructGeneralizedType(contentType, contentClass);
-                       } else if (currRaw.isAssignableFrom(contentClass)) { // specialization, ok as well
-                           contentType = tf.constructSpecializedType(contentType, contentClass);
-                       } else {
-                           throw new JsonMappingException(null,
-                                   String.format("Can not refine serialization content type %s into %s; types not related",
-                                           contentType, contentClass.getName()));
-                       }
-                   } catch (IllegalArgumentException iae) { // shouldn't really happen
-                       throw new JsonMappingException(null,
-                               String.format("Internal error: failed to refine value type of %s with concrete-type annotation (value %s), from '%s': %s",
-                                       type, contentClass.getName(), a.getName(), iae.getMessage()),
-                                       iae);
-                   }
-               }
-               type = type.withContentType(contentType);
-           }
-        }
-        return type;
-    }
-
+    
     /*
     /**********************************************************
     /* Serialization: class annotations
@@ -991,14 +875,6 @@
      * @since 2.1
      */
     public PropertyName findNameForSerialization(Annotated a) {
-        /*
-        if (name != null) {
-            if (name.length() == 0) { // empty String means 'default'
-                return PropertyName.USE_DEFAULT;
-            }
-            return new PropertyName(name);
-        }
-        */
         return null;
     }
 
@@ -1008,11 +884,71 @@
      * should be used as "the value" of the object instance; usually
      * serialized as a primitive value such as String or number.
      *
-     * @return True if such annotation is found (and is not disabled);
-     *   false if no enabled annotation is found
+     * @return {@link Boolean#TRUE} if such annotation is found and is not disabled;
+     *   {@link Boolean#FALSE} if disabled annotation (block) is found (to indicate
+     *   accessor is definitely NOT to be used "as value"); or `null` if no
+     *   information found.
+     *   
+     * @since 2.9
      */
-    public boolean hasAsValueAnnotation(AnnotatedMethod am) {
-        return false;
+    public Boolean hasAsValue(Annotated a) {
+        // 20-Nov-2016, tatu: Delegate in 2.9; remove redirect from later versions
+        if (a instanceof AnnotatedMethod) {
+            if (hasAsValueAnnotation((AnnotatedMethod) a)) {
+                return true;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Method for checking whether given method has an annotation
+     * that suggests that the method is to serve as "any setter";
+     * method to be used for accessing set of miscellaneous "extra"
+     * properties, often bound with matching "any setter" method.
+     *
+     * @return True if such annotation is found (and is not disabled),
+     *   false otherwise
+     *
+     * @since 2.9
+     */
+    public Boolean hasAnyGetter(Annotated a) {
+
+        // 21-Nov-2016, tatu: Delegate in 2.9; remove redirect from later versions
+        if (a instanceof AnnotatedMethod) {
+            if (hasAnyGetterAnnotation((AnnotatedMethod) a)) {
+                return true;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Method for efficiently figuring out which if given set of <code>Enum</code> values
+     * have explicitly defined name. Method will overwrite entries in incoming <code>names</code>
+     * array with explicit names found, if any, leaving other entries unmodified.
+     *<p>
+     * Default implementation will simply delegate to {@link #findEnumValue}, which is close
+     * enough, although unfortunately NOT 100% equivalent (as it will also consider <code>name()</code>
+     * to give explicit value).
+     *
+     * @since 2.7
+     */
+    public String[] findEnumValues(Class<?> enumType, Enum<?>[] enumValues, String[] names) {
+        // 18-Oct-2016, tatu: In 2.8 delegated to deprecated method; not so in 2.9 and beyond
+        return names;
+    }
+
+    /**
+     * Finds the Enum value that should be considered the default value, if possible.
+     *
+     * @param enumCls The Enum class to scan for the default value.
+     * @return null if none found or it's not possible to determine one.
+     *
+     * @since 2.8
+     */
+    public Enum<?> findDefaultEnumValue(Class<Enum<?>> enumCls) {
+        return null;
     }
 
     /**
@@ -1033,42 +969,21 @@
     }
 
     /**
-     * Method for efficiently figuring out which if given set of <code>Enum</code> values
-     * have explicitly defined name. Method will overwrite entries in incoming <code>names</code>
-     * array with explicit names found, if any, leaving other entries unmodified.
-     *<p>
-     * Default implementation will simply delegate to {@link #findEnumValue}, which is close
-     * enough, although unfortunately NOT 100% equivalent (as it will also consider <code>name()</code>
-     * to give explicit value).
-     *
-     * @since 2.7
+     * @deprecated Since 2.9 Use {@link #hasAsValue(Annotated)} instead.
      */
-    public  String[] findEnumValues(Class<?> enumType, Enum<?>[] enumValues, String[] names) {
-        for (int i = 0, len = enumValues.length; i < len; ++i) {
-            /* 12-Mar-2016, tatu: This is quite tricky, considering that we should NOT
-             *   overwrite values with default `name`... so for now, let's only delegate
-             *   if no value has been set. Still not optimal but has to do
-             */
-            // TODO: In 2.8, stop delegation?
-            if (names[i] == null) {
-                names[i] = findEnumValue(enumValues[i]);
-            }
-        }
-        return names;
+    @Deprecated // since 2.9
+    public boolean hasAsValueAnnotation(AnnotatedMethod am) {
+        return false;
     }
 
     /**
-     * Finds the Enum value that should be considered the default value, if possible.
-     *
-     * @param enumCls The Enum class to scan for the default value.
-     * @return null if none found or it's not possible to determine one.
-     *
-     * @since 2.8
+     * @deprecated Since 2.9 Use {@link #hasAnyGetter} instead
      */
-    public Enum<?> findDefaultEnumValue(Class<Enum<?>> enumCls) {
-        return null;
+    @Deprecated
+    public boolean hasAnyGetterAnnotation(AnnotatedMethod am) {
+        return false;
     }
-
+    
     /*
     /**********************************************************
     /* Deserialization: general annotations
@@ -1175,65 +1090,9 @@
     public JavaType refineDeserializationType(final MapperConfig<?> config,
             final Annotated a, final JavaType baseType) throws JsonMappingException
     {
-        JavaType type = baseType;
-        final TypeFactory tf = config.getTypeFactory();
-
-        // 10-Oct-2015, tatu: For 2.7, we'll need to delegate back to
-        //    now-deprecated secondary methods; this because while
-        //    direct sub-class not yet retrofitted may only override
-        //    those methods. With 2.8 or later we may consider removal
-        //    of these methods
-
-        
-        // Ok: start by refining the main type itself; common to all types
-        Class<?> valueClass = findDeserializationType(a, type);
-        if ((valueClass != null) && !type.hasRawClass(valueClass)) {
-            try {
-                type = tf.constructSpecializedType(type, valueClass);
-            } catch (IllegalArgumentException iae) {
-                throw new JsonMappingException(null,
-                        String.format("Failed to narrow type %s with annotation (value %s), from '%s': %s",
-                                type, valueClass.getName(), a.getName(), iae.getMessage()),
-                                iae);
-            }
-        }
-        // Then further processing for container types
-
-        // First, key type (for Maps, Map-like types):
-        if (type.isMapLikeType()) {
-            JavaType keyType = type.getKeyType();
-            Class<?> keyClass = findDeserializationKeyType(a, keyType);
-            if (keyClass != null) {
-                try {
-                    keyType = tf.constructSpecializedType(keyType, keyClass);
-                    type = ((MapLikeType) type).withKeyType(keyType);
-                } catch (IllegalArgumentException iae) {
-                    throw new JsonMappingException(null,
-                            String.format("Failed to narrow key type of %s with concrete-type annotation (value %s), from '%s': %s",
-                                    type, keyClass.getName(), a.getName(), iae.getMessage()),
-                                    iae);
-                }
-            }
-        }
-        JavaType contentType = type.getContentType();
-        if (contentType != null) { // collection[like], map[like], array, reference
-            // And then value types for all containers:
-           Class<?> contentClass = findDeserializationContentType(a, contentType);
-           if (contentClass != null) {
-               try {
-                   contentType = tf.constructSpecializedType(contentType, contentClass);
-                   type = type.withContentType(contentType);
-               } catch (IllegalArgumentException iae) {
-                   throw new JsonMappingException(null,
-                           String.format("Failed to narrow value type of %s with concrete-type annotation (value %s), from '%s': %s",
-                                   type, contentClass.getName(), a.getName(), iae.getMessage()),
-                                   iae);
-               }
-           }
-        }
-        return type;
+        return baseType;
     }
-    
+
     /**
      * Method for accessing annotated type definition that a
      * property can have, to be used as the type for deserialization
@@ -1325,7 +1184,7 @@
     public JsonPOJOBuilder.Value findPOJOBuilderConfig(AnnotatedClass ac) {
         return null;
     }
-    
+
     /*
     /**********************************************************
     /* Deserialization: property annotations
@@ -1347,14 +1206,6 @@
      * @since 2.1
      */
     public PropertyName findNameForDeserialization(Annotated a) {
-        /*
-        if (name != null) {
-            if (name.length() == 0) { // empty String means 'default'
-                return PropertyName.USE_DEFAULT;
-            }
-            return new PropertyName(name);
-        }
-        */
         return null;
     }
     
@@ -1366,24 +1217,60 @@
      *
      * @return True if such annotation is found (and is not disabled),
      *   false otherwise
+     *   
+     * @since 2.9
      */
-    public boolean hasAnySetterAnnotation(AnnotatedMethod am) {
-        return false;
+    public Boolean hasAnySetter(Annotated a) {
+        return null;
     }
 
     /**
-     * Method for checking whether given method has an annotation
-     * that suggests that the method is to serve as "any setter";
-     * method to be used for accessing set of miscellaneous "extra"
-     * properties, often bound with matching "any setter" method.
+     * Method for finding possible settings for property, given annotations
+     * on an accessor.
      *
-     * @return True if such annotation is found (and is not disabled),
-     *   false otherwise
+     * @since 2.9
      */
-    public boolean hasAnyGetterAnnotation(AnnotatedMethod am) {
-        return false;
+    public JsonSetter.Value findSetterInfo(Annotated a) {
+        return JsonSetter.Value.empty();
     }
-    
+
+    /**
+     * Method for finding merge settings for property, if any.
+     *
+     * @since 2.9
+     */
+    public Boolean findMergeInfo(Annotated a) {
+        return null;
+    }
+
+    /**
+     * Method called to check whether potential Creator (constructor or static factory
+     * method) has explicit annotation to indicate it as actual Creator; and if so,
+     * which {@link com.fasterxml.jackson.annotation.JsonCreator.Mode} to use.
+     *<p>
+     * NOTE: caller needs to consider possibility of both `null` (no annotation found)
+     * and {@link com.fasterxml.jackson.annotation.JsonCreator.Mode#DISABLED} (annotation found,
+     * but disabled); latter is necessary as marker in case multiple introspectors are chained,
+     * as well as possibly as when using mix-in annotations.
+     *
+     * @param config Configuration settings in effect (for serialization or deserialization)
+     * @param a Annotated accessor (usually constructor or static method) to check
+     *
+     * @since 2.9
+     */
+    public JsonCreator.Mode findCreatorAnnotation(MapperConfig<?> config, Annotated a) {
+        // 13-Sep-2016, tatu: for backwards compatibility, implement using delegation
+        ///   (remove from version AFTER 2.9)
+        if (hasCreatorAnnotation(a)) {
+            JsonCreator.Mode mode = findCreatorBinding(a);
+            if (mode == null) {
+                mode = JsonCreator.Mode.DEFAULT;
+            }
+            return mode;
+        }
+        return null;
+    }
+
     /**
      * Method for checking whether given annotated item (method, constructor)
      * has an annotation
@@ -1393,7 +1280,10 @@
      *
      * @return True if such annotation is found (and is not disabled),
      *   false otherwise
+     *
+     * @deprecated Since 2.9 use {@link #findCreatorAnnotation} instead.
      */
+    @Deprecated
     public boolean hasCreatorAnnotation(Annotated a) {
         return false;
     }
@@ -1405,11 +1295,21 @@
      * creator with implicit but no explicit name for the argument).
      * 
      * @since 2.5
+     * @deprecated Since 2.9 use {@link #findCreatorAnnotation} instead.
      */
+    @Deprecated
     public JsonCreator.Mode findCreatorBinding(Annotated a) {
         return null;
     }
-    
+
+    /**
+     * @deprecated Since 2.9 use {@link #hasAnySetter} instead.
+     */
+    @Deprecated // since 2.9
+    public boolean hasAnySetterAnnotation(AnnotatedMethod am) {
+        return false;
+    }
+
     /*
     /**********************************************************
     /* Overridable methods: may be used as low-level extension
diff --git a/src/main/java/com/fasterxml/jackson/databind/BeanDescription.java b/src/main/java/com/fasterxml/jackson/databind/BeanDescription.java
index cd21182..24ddfe9 100644
--- a/src/main/java/com/fasterxml/jackson/databind/BeanDescription.java
+++ b/src/main/java/com/fasterxml/jackson/databind/BeanDescription.java
@@ -23,7 +23,7 @@
 {
     /**
      * Bean type information, including raw class and possible
-     * * generics information
+     * generics information
      */
     protected final JavaType _type;
 
@@ -114,12 +114,22 @@
      */
     public abstract List<BeanPropertyDefinition> findProperties();
 
+    public abstract Set<String> getIgnoredPropertyNames();
+
     /**
      * Method for locating all back-reference properties (setters, fields) bean has
+     *
+     * @since 2.9
      */
-    public abstract Map<String,AnnotatedMember> findBackReferenceProperties();
+    public abstract List<BeanPropertyDefinition> findBackReferences();
 
-    public abstract Set<String> getIgnoredPropertyNames();
+    /**
+     * Method for locating all back-reference properties (setters, fields) bean has
+     *
+     * @deprecated Since 2.9 use {@link #findBackReferences()} instead
+     */
+    @Deprecated
+    public abstract Map<String,AnnotatedMember> findBackReferenceProperties();
 
     /*
     /**********************************************************
@@ -162,39 +172,63 @@
     /* Basic API for finding property accessors
     /**********************************************************
      */
-    
-    public abstract AnnotatedMember findAnyGetter();
 
     /**
-     * Method used to locate the method of introspected class that
-     * implements {@link com.fasterxml.jackson.annotation.JsonAnySetter}. If no such method exists
-     * null is returned. If more than one are found, an exception
-     * is thrown.
-     * Additional checks are also made to see that method signature
-     * is acceptable: needs to take 2 arguments, first one String or
-     * Object; second any can be any type.
-     */
-    public abstract AnnotatedMethod findAnySetter();
-
-    /**
-     * Method used to locate the field of the class that implements
-     * {@link com.fasterxml.jackson.annotation.JsonAnySetter} If no such method
-     * exists null is returned. If more than one are found, an exception is thrown.
-     * 
-     * @since 2.8
-     */
-    public abstract AnnotatedMember findAnySetterField();
-
-    /**
-     * Method for locating the getter method that is annotated with
+     * Method for locating accessor (readable field, or "getter" method)
+     * that has
      * {@link com.fasterxml.jackson.annotation.JsonValue} annotation,
      * if any. If multiple ones are found,
      * an error is reported by throwing {@link IllegalArgumentException}
+     *
+     * @since 2.9
      */
-    public abstract AnnotatedMethod findJsonValueMethod();
+    public abstract AnnotatedMember findJsonValueAccessor();
+
+    public abstract AnnotatedMember findAnyGetter();
+
+    /**
+     * Method used to locate a mutator (settable field, or 2-argument set method)
+     * of introspected class that
+     * implements {@link com.fasterxml.jackson.annotation.JsonAnySetter}.
+     * If no such mutator exists null is returned. If more than one are found,
+     * an exception is thrown.
+     * Additional checks are also made to see that method signature
+     * is acceptable: needs to take 2 arguments, first one String or
+     * Object; second any can be any type.
+     *
+     * @since 2.9
+     */
+    public abstract AnnotatedMember findAnySetterAccessor();
 
     public abstract AnnotatedMethod findMethod(String name, Class<?>[] paramTypes);
+
+    @Deprecated // since 2.9
+    public abstract AnnotatedMethod findJsonValueMethod();
     
+    /**
+     * @deprecated Since 2.9: use {@link #findAnySetterAccessor} instead
+     */
+    @Deprecated
+    public AnnotatedMethod findAnySetter() {
+        AnnotatedMember m = findAnySetterAccessor();
+        if (m instanceof AnnotatedMethod) {
+            return (AnnotatedMethod) m;
+        }
+        return null;
+    }
+
+    /**
+     * @deprecated Since 2.9: use {@link #findAnySetterAccessor} instead
+     */
+    @Deprecated
+    public AnnotatedMember findAnySetterField() {
+        AnnotatedMember m = findAnySetterAccessor();
+        if (m instanceof AnnotatedField) {
+            return m;
+        }
+        return null;
+    }
+
     /*
     /**********************************************************
     /* Basic API, class configuration
@@ -279,4 +313,13 @@
      *   suitable default constructor was found; null otherwise.
      */
     public abstract Object instantiateBean(boolean fixAccess);
+
+    /**
+     * Method for finding out if the POJO specifies default view(s) to
+     * use for properties, considering both per-type annotations and
+     * global default settings.
+     *
+     * @since 2.9
+     */
+    public abstract Class<?>[] findDefaultViews();
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/BeanProperty.java b/src/main/java/com/fasterxml/jackson/databind/BeanProperty.java
index 525aa11..e8a562c 100644
--- a/src/main/java/com/fasterxml/jackson/databind/BeanProperty.java
+++ b/src/main/java/com/fasterxml/jackson/databind/BeanProperty.java
@@ -1,12 +1,17 @@
 package com.fasterxml.jackson.databind;
 
 import java.lang.annotation.Annotation;
+import java.util.Collections;
+import java.util.List;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonFormat.Value;
 import com.fasterxml.jackson.annotation.JsonInclude;
+
 import com.fasterxml.jackson.databind.cfg.MapperConfig;
 import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor;
+import com.fasterxml.jackson.databind.type.TypeFactory;
 import com.fasterxml.jackson.databind.util.Annotations;
 import com.fasterxml.jackson.databind.util.Named;
 
@@ -14,7 +19,7 @@
  * Bean properties are logical entities that represent data
  * that Java objects (POJOs (Plain Old Java Objects), sometimes also called "beans")
  * contain; and that are accessed using accessors (methods like getters
- * and setters, fields, constructor parametrers).
+ * and setters, fields, constructor parameters).
  * Instances allow access to annotations directly associated
  * to property (via field or method), as well as contextual
  * annotations (annotations for class that contains properties).
@@ -26,7 +31,7 @@
  * {@link com.fasterxml.jackson.databind.deser.ContextualDeserializer}
  * resolution occurs (<code>createContextual(...)</code> method is called).
  * References may (need to) be retained by serializers and deserializers,
- * especially when further resolving dependant handlers like value
+ * especially when further resolving dependent handlers like value
  * serializers/deserializers or structured types.
  */
 public interface BeanProperty extends Named
@@ -161,6 +166,16 @@
      */
     public JsonInclude.Value findPropertyInclusion(MapperConfig<?> config, Class<?> baseType);
 
+    /**
+     * Method for accessing set of possible alternate names that are accepted
+     * during deserialization.
+     *
+     * @return List (possibly empty) of alternate names; never null
+     *
+     * @since 2.9
+     */
+    public List<PropertyName> findAliases(MapperConfig<?> config);
+
     /*
     /**********************************************************
     /* Schema/introspection support
@@ -196,8 +211,11 @@
      * Simple stand-alone implementation, useful as a placeholder
      * or base class for more complex implementations.
      */
-    public static class Std implements BeanProperty
+    public static class Std implements BeanProperty,
+        java.io.Serializable // 2.9
     {
+        private static final long serialVersionUID = 1L;
+
         protected final PropertyName _name;
         protected final JavaType _type;
         protected final PropertyName _wrapperName;
@@ -211,29 +229,32 @@
          */
         protected final AnnotatedMember _member;
 
-        /**
-         * Annotations defined in the context class (if any); may be null
-         * if no annotations were found
-         */
-        protected final Annotations _contextAnnotations;
-
         public Std(PropertyName name, JavaType type, PropertyName wrapperName,
-                Annotations contextAnnotations, AnnotatedMember member,
-                PropertyMetadata metadata)
+                AnnotatedMember member, PropertyMetadata metadata)
         {
             _name = name;
             _type = type;
             _wrapperName = wrapperName;
             _metadata = metadata;
             _member = member;
-            _contextAnnotations = contextAnnotations;
+        }
+
+        /**
+         * @deprecated Since 2.9
+         */
+        @Deprecated
+        public Std(PropertyName name, JavaType type, PropertyName wrapperName,
+                Annotations contextAnnotations,
+                AnnotatedMember member, PropertyMetadata metadata)
+        {
+            this(name, type, wrapperName, member, metadata);
         }
 
         /**
          * @since 2.6
          */
         public Std(Std base, JavaType newType) {
-            this(base._name, newType, base._wrapperName, base._contextAnnotations, base._member, base._metadata);
+            this(base._name, newType, base._wrapperName, base._member, base._metadata);
         }
 
         public Std withType(JavaType type) {
@@ -247,7 +268,7 @@
 
         @Override
         public <A extends Annotation> A getContextAnnotation(Class<A> acls) {
-            return (_contextAnnotations == null) ? null : _contextAnnotations.get(acls);
+            return null;
         }
 
         @Override
@@ -279,7 +300,7 @@
         @Override
         public JsonInclude.Value findPropertyInclusion(MapperConfig<?> config, Class<?> baseType)
         {
-            JsonInclude.Value v0 = config.getDefaultPropertyInclusion(baseType);
+            JsonInclude.Value v0 = config.getDefaultInclusion(baseType, _type.getRawClass());
             AnnotationIntrospector intr = config.getAnnotationIntrospector();
             if ((intr == null) || (_member == null)) {
                 return v0;
@@ -291,6 +312,13 @@
             return v0.withOverrides(v);
         }
 
+        @Override
+        public List<PropertyName> findAliases(MapperConfig<?> config) {
+            // 26-Feb-2017, tatu: Do we really need to allow actual definition?
+            //    For now, let's not.
+            return Collections.emptyList();
+        }
+
         @Override public String getName() { return _name.getSimpleName(); }
         @Override public PropertyName getFullName() { return _name; }
         @Override public JavaType getType() { return _type; }
@@ -314,4 +342,91 @@
             throw new UnsupportedOperationException("Instances of "+getClass().getName()+" should not get visited");
         }
     }
+
+    /**
+     * Alternative "Null" implementation that can be used in cases where a non-null
+     * {@link BeanProperty} is needed
+     *
+     * @since 2.9
+     */
+    public static class Bogus implements BeanProperty
+    {
+        @Override
+        public String getName() {
+            return "";
+        }
+
+        @Override
+        public PropertyName getFullName() {
+            return PropertyName.NO_NAME;
+        }
+
+        @Override
+        public JavaType getType() {
+            return TypeFactory.unknownType();
+        }
+
+        @Override
+        public PropertyName getWrapperName() {
+            return null;
+        }
+
+        @Override
+        public PropertyMetadata getMetadata() {
+            return PropertyMetadata.STD_REQUIRED_OR_OPTIONAL;
+        }
+
+        @Override
+        public boolean isRequired() {
+            return false;
+        }
+
+        @Override
+        public boolean isVirtual() {
+            return false;
+        }
+
+        @Override
+        public <A extends Annotation> A getAnnotation(Class<A> acls) {
+            return null;
+        }
+
+        @Override
+        public <A extends Annotation> A getContextAnnotation(Class<A> acls) {
+            return null;
+        }
+
+        @Override
+        public AnnotatedMember getMember() {
+            return null;
+        }
+
+        @Override
+        @Deprecated
+        public Value findFormatOverrides(AnnotationIntrospector intr) {
+            return Value.empty();
+        }
+
+        @Override
+        public Value findPropertyFormat(MapperConfig<?> config, Class<?> baseType) {
+            return Value.empty();
+        }
+
+        @Override
+        public com.fasterxml.jackson.annotation.JsonInclude.Value findPropertyInclusion(
+                MapperConfig<?> config, Class<?> baseType)
+        {
+            return null;
+        }
+
+        @Override
+        public List<PropertyName> findAliases(MapperConfig<?> config) {
+            return Collections.emptyList();
+        }
+
+        @Override
+        public void depositSchemaProperty(JsonObjectFormatVisitor objectVisitor,
+                SerializerProvider provider) throws JsonMappingException {
+        }
+    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/DatabindContext.java b/src/main/java/com/fasterxml/jackson/databind/DatabindContext.java
index f42a0bb..9e4d096 100644
--- a/src/main/java/com/fasterxml/jackson/databind/DatabindContext.java
+++ b/src/main/java/com/fasterxml/jackson/databind/DatabindContext.java
@@ -25,6 +25,15 @@
  */
 public abstract class DatabindContext
 {
+    /**
+     * Let's limit length of error messages, for cases where underlying data
+     * may be very large -- no point in spamming logs with megabytes of meaningless
+     * data.
+     *
+     * @since 2.9
+     */
+    private final static int MAX_ERROR_STR_LEN = 500;
+
     /*
     /**********************************************************
     /* Generic config access
@@ -134,7 +143,10 @@
      * type (usually {@link java.lang.Class})
      */
     public JavaType constructType(Type type) {
-         return getTypeFactory().constructType(type);
+        if (type == null) {
+            return null;
+        }
+        return getTypeFactory().constructType(type);
     }
 
     /**
@@ -149,6 +161,59 @@
         return getConfig().constructSpecializedType(baseType, subclass);
     }
 
+    /**
+     * Lookup method called when code needs to resolve class name from input;
+     * usually simple lookup
+     *
+     * @since 2.9
+     */
+    public JavaType resolveSubType(JavaType baseType, String subClass)
+        throws JsonMappingException
+    {
+        // 30-Jan-2010, tatu: Most ids are basic class names; so let's first
+        //    check if any generics info is added; and only then ask factory
+        //    to do translation when necessary
+        if (subClass.indexOf('<') > 0) {
+            // note: may want to try combining with specialization (esp for EnumMap)?
+            // 17-Aug-2017, tatu: As per [databind#1735] need to ensure assignment
+            //    compatibility -- needed later anyway, and not doing so may open
+            //    security issues.
+            JavaType t = getTypeFactory().constructFromCanonical(subClass);
+            if (t.isTypeOrSubTypeOf(baseType.getRawClass())) {
+                return t;
+            }
+        } else {
+            Class<?> cls;
+            try {
+                cls =  getTypeFactory().findClass(subClass);
+            } catch (ClassNotFoundException e) { // let caller handle this problem
+                return null;
+            } catch (Exception e) {
+                throw invalidTypeIdException(baseType, subClass, String.format(
+                        "problem: (%s) %s",
+                        e.getClass().getName(), e.getMessage()));
+            }
+            if (baseType.isTypeOrSuperTypeOf(cls)) {
+                return getTypeFactory().constructSpecializedType(baseType, cls);
+            }
+        }
+        throw invalidTypeIdException(baseType, subClass, "Not a subtype");
+    }
+
+    /**
+     * Helper method for constructing exception to indicate that given type id
+     * could not be resolved to a valid subtype of specified base type.
+     * Most commonly called during polymorphic deserialization.
+     *<p>
+     * Note that most of the time this method should NOT be called directly: instead,
+     * method <code>handleUnknownTypeId()</code> should be called which will call this method
+     * if necessary.
+     *
+     * @since 2.9
+     */
+    protected abstract JsonMappingException invalidTypeIdException(JavaType baseType, String typeId,
+            String extraDesc);
+
     public abstract TypeFactory getTypeFactory();
 
     /*
@@ -224,4 +289,87 @@
         }
         return (Converter<Object,Object>) conv;
     }
+
+    /*
+    /**********************************************************
+    /* Error reporting
+    /**********************************************************
+     */
+
+    /**
+     * Helper method called to indicate a generic problem that stems from type
+     * definition(s), not input data, or input/output state; typically this
+     * means throwing a {@link com.fasterxml.jackson.databind.exc.InvalidDefinitionException}.
+     *
+     * @since 2.9
+     */
+    public abstract <T> T reportBadDefinition(JavaType type, String msg) throws JsonMappingException;
+
+    /**
+     * @since 2.9
+     */
+    public <T> T reportBadDefinition(Class<?> type, String msg) throws JsonMappingException {
+        return reportBadDefinition(constructType(type), msg);
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+
+    /**
+     * @since 2.9
+     */
+    protected final String _format(String msg, Object... msgArgs) {
+        if (msgArgs.length > 0) {
+            return String.format(msg, msgArgs);
+        }
+        return msg;
+    }
+
+    /**
+     * @since 2.9
+     */
+    protected final String _truncate(String desc) {
+        if (desc == null) {
+            return "";
+        }
+        if (desc.length() <= MAX_ERROR_STR_LEN) {
+            return desc;
+        }
+        return desc.substring(0, MAX_ERROR_STR_LEN) + "]...[" + desc.substring(desc.length() - MAX_ERROR_STR_LEN);
+    }
+
+    /**
+     * @since 2.9
+     */
+    protected String _quotedString(String desc) {
+        if (desc == null) {
+            return "[N/A]";
+        }
+        // !!! should we quote it? (in case there are control chars, linefeeds)
+        return String.format("\"%s\"", _truncate(desc));
+    }
+    
+    /**
+     * @since 2.9
+     */
+    protected String _colonConcat(String msgBase, String extra) {
+        if (extra == null) {
+            return msgBase;
+        }
+        return msgBase + ": " + extra;
+    }
+
+    /**
+     * @since 2.9
+     */
+    protected String _desc(String desc) {
+        if (desc == null) {
+            return "[N/A]";
+        }
+        // !!! should we quote it? (in case there are control chars, linefeeds)
+        return _truncate(desc);
+    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/DeserializationConfig.java b/src/main/java/com/fasterxml/jackson/databind/DeserializationConfig.java
index 68556ad..8867935 100644
--- a/src/main/java/com/fasterxml/jackson/databind/DeserializationConfig.java
+++ b/src/main/java/com/fasterxml/jackson/databind/DeserializationConfig.java
@@ -1,10 +1,7 @@
 package com.fasterxml.jackson.databind;
 
-import java.text.DateFormat;
 import java.util.*;
 
-import com.fasterxml.jackson.annotation.*;
-
 import com.fasterxml.jackson.core.*;
 
 import com.fasterxml.jackson.databind.cfg.*;
@@ -12,7 +9,6 @@
 import com.fasterxml.jackson.databind.introspect.*;
 import com.fasterxml.jackson.databind.jsontype.*;
 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
-import com.fasterxml.jackson.databind.type.TypeFactory;
 import com.fasterxml.jackson.databind.util.LinkedNode;
 import com.fasterxml.jackson.databind.util.RootNameLookup;
 
@@ -29,22 +25,22 @@
     extends MapperConfigBase<DeserializationFeature, DeserializationConfig>
     implements java.io.Serializable // since 2.1
 {
-    // since 2.5
-    private static final long serialVersionUID = 1;
+    // since 2.9
+    private static final long serialVersionUID = 2;
 
     /*
     /**********************************************************
     /* Configured helper objects
     /**********************************************************
      */
-    
+
     /**
      * Linked list that contains all registered problem handlers.
      * Implementation as front-added linked list allows for sharing
      * of the list (tail) without copying the list.
      */
     protected final LinkedNode<DeserializationProblemHandler> _problemHandlers;
-    
+
     /**
      * Factory used for constructing {@link com.fasterxml.jackson.databind.JsonNode} instances.
      */
@@ -60,13 +56,13 @@
      * Set of {@link DeserializationFeature}s enabled.
      */
     protected final int _deserFeatures;
-    
+
     /*
     /**********************************************************
     /* Parser features: generic, format-specific
     /**********************************************************
      */
-    
+
     /**
      * States of {@link com.fasterxml.jackson.core.JsonParser.Feature}s to enable/disable.
      */
@@ -93,7 +89,7 @@
 
     /*
     /**********************************************************
-    /* Life-cycle, constructors
+    /* Life-cycle, primary constructors for new instances
     /**********************************************************
      */
 
@@ -101,8 +97,8 @@
      * Constructor used by ObjectMapper to create default configuration object instance.
      */
     public DeserializationConfig(BaseSettings base,
-            SubtypeResolver str, SimpleMixInResolver mixins,
-            RootNameLookup rootNames, ConfigOverrides configOverrides)
+            SubtypeResolver str, SimpleMixInResolver mixins, RootNameLookup rootNames,
+            ConfigOverrides configOverrides)
     {
         super(base, str, mixins, rootNames, configOverrides);
         _deserFeatures = collectFeatureDefaults(DeserializationFeature.class);
@@ -115,14 +111,31 @@
     }
 
     /**
-     * @deprecated Since 2.8, remove from 2.9 or later
+     * Copy-constructor used for making a copy used by new {@link ObjectMapper}.
+     *
+     * @since 2.9
      */
-    @Deprecated
-    public DeserializationConfig(BaseSettings base, SubtypeResolver str,
-            SimpleMixInResolver mixins, RootNameLookup rootNames) {
-        this(base, str, mixins, rootNames, null);
+    protected DeserializationConfig(DeserializationConfig src,
+            SimpleMixInResolver mixins, RootNameLookup rootNames,
+            ConfigOverrides configOverrides)
+    {
+        super(src, mixins, rootNames, configOverrides);
+        _deserFeatures = src._deserFeatures;
+        _problemHandlers = src._problemHandlers;
+        _nodeFactory = src._nodeFactory;
+        _parserFeatures = src._parserFeatures;
+        _parserFeaturesToChange = src._parserFeaturesToChange;
+        _formatReadFeatures = src._formatReadFeatures;
+        _formatReadFeaturesToChange = src._formatReadFeaturesToChange;
     }
-    
+
+    /*
+    /**********************************************************
+    /* Life-cycle, secondary constructors to support
+    /* "mutant factories", with single property changes
+    /**********************************************************
+     */
+
     private DeserializationConfig(DeserializationConfig src,
             int mapperFeatures, int deserFeatures,
             int parserFeatures, int parserFeatureMask,
@@ -239,109 +252,37 @@
         _formatReadFeaturesToChange = src._formatReadFeaturesToChange;
     }
 
-    /**
-     * Copy-constructor used for making a copy used by new {@link ObjectMapper}.
-     *
-     * @since 2.8
-     */
-    protected DeserializationConfig(DeserializationConfig src, SimpleMixInResolver mixins,
-            RootNameLookup rootNames, ConfigOverrides configOverrides)
-    {
-        super(src, mixins, rootNames, configOverrides);
-        _deserFeatures = src._deserFeatures;
-        _problemHandlers = src._problemHandlers;
-        _nodeFactory = src._nodeFactory;
-        _parserFeatures = src._parserFeatures;
-        _parserFeaturesToChange = src._parserFeaturesToChange;
-        _formatReadFeatures = src._formatReadFeatures;
-        _formatReadFeaturesToChange = src._formatReadFeaturesToChange;
-    }
-
     // for unit tests only:
     protected BaseSettings getBaseSettings() { return _base; }
 
     /*
     /**********************************************************
-    /* Life-cycle, factory methods from MapperConfig
+    /* Life-cycle, general factory methods from MapperConfig(Base)
     /**********************************************************
      */
-    
-    @Override
-    public DeserializationConfig with(MapperFeature... features)
-    {
-        int newMapperFlags = _mapperFeatures;
-        for (MapperFeature f : features) {
-            newMapperFlags |= f.getMask();
-        }
-        return (newMapperFlags == _mapperFeatures) ? this :
-            new DeserializationConfig(this, newMapperFlags, _deserFeatures,
-                    _parserFeatures, _parserFeaturesToChange,
-                    _formatReadFeatures, _formatReadFeaturesToChange);
-                    
+
+    @Override // since 2.9
+    protected final DeserializationConfig _withBase(BaseSettings newBase) {
+        return (_base == newBase) ? this : new DeserializationConfig(this, newBase);
     }
 
-    @Override
-    public DeserializationConfig without(MapperFeature... features)
-    {
-        int newMapperFlags = _mapperFeatures;
-        for (MapperFeature f : features) {
-             newMapperFlags &= ~f.getMask();
-        }
-        return (newMapperFlags == _mapperFeatures) ? this :
-            new DeserializationConfig(this, newMapperFlags, _deserFeatures,
-                    _parserFeatures, _parserFeaturesToChange,
-                    _formatReadFeatures, _formatReadFeaturesToChange);
+    @Override // since 2.9
+    protected final DeserializationConfig _withMapperFeatures(int mapperFeatures) {
+        return new DeserializationConfig(this, mapperFeatures, _deserFeatures,
+                        _parserFeatures, _parserFeaturesToChange,
+                        _formatReadFeatures, _formatReadFeaturesToChange);
     }
 
-    @Override
-    public DeserializationConfig with(MapperFeature feature, boolean state)
-    {
-        int newMapperFlags;
-        if (state) {
-            newMapperFlags = _mapperFeatures | feature.getMask();
-        } else {
-            newMapperFlags = _mapperFeatures & ~feature.getMask();
-        }
-        return (newMapperFlags == _mapperFeatures) ? this :
-            new DeserializationConfig(this, newMapperFlags, _deserFeatures,
-                    _parserFeatures, _parserFeaturesToChange,
-                    _formatReadFeatures, _formatReadFeaturesToChange);
-    }
-
-    @Override
-    public DeserializationConfig with(ClassIntrospector ci) {
-        return _withBase(_base.withClassIntrospector(ci));
-    }
-
-    @Override
-    public DeserializationConfig with(AnnotationIntrospector ai) {
-        return _withBase(_base.withAnnotationIntrospector(ai));
-    }
-
-    @Override
-    public DeserializationConfig with(VisibilityChecker<?> vc) {
-        return _withBase(_base.withVisibilityChecker(vc));
-    }
-
-    @Override
-    public DeserializationConfig withVisibility(PropertyAccessor forMethod, JsonAutoDetect.Visibility visibility) {
-        return _withBase( _base.withVisibility(forMethod, visibility));
-    }
-    
-    @Override
-    public DeserializationConfig with(TypeResolverBuilder<?> trb) {
-        return _withBase(_base.withTypeResolverBuilder(trb));
-    }
+    /*
+    /**********************************************************
+    /* Life-cycle, specific factory methods from MapperConfig
+    /**********************************************************
+     */
 
     @Override
     public DeserializationConfig with(SubtypeResolver str) {
         return (_subtypeResolver == str) ? this : new DeserializationConfig(this, str);
     }
-    
-    @Override
-    public DeserializationConfig with(PropertyNamingStrategy pns) {
-        return _withBase(_base.withPropertyNamingStrategy(pns));
-    }
 
     @Override
     public DeserializationConfig withRootName(PropertyName rootName) {
@@ -356,58 +297,14 @@
     }
 
     @Override
-    public DeserializationConfig with(TypeFactory tf) {
-        return _withBase( _base.withTypeFactory(tf));
-    }
-
-    @Override
-    public DeserializationConfig with(DateFormat df) {
-        return _withBase(_base.withDateFormat(df));
-    }
-    
-    @Override
-    public DeserializationConfig with(HandlerInstantiator hi) {
-        return _withBase(_base.withHandlerInstantiator(hi));
-    }
-
-    @Override
-    public DeserializationConfig withInsertedAnnotationIntrospector(AnnotationIntrospector ai) {
-        return _withBase(_base.withInsertedAnnotationIntrospector(ai));
-    }
-
-    @Override
-    public DeserializationConfig withAppendedAnnotationIntrospector(AnnotationIntrospector ai) {
-        return _withBase(_base.withAppendedAnnotationIntrospector(ai));
-    }
-
-    @Override
     public DeserializationConfig withView(Class<?> view) {
         return (_view == view) ? this : new DeserializationConfig(this, view);
     }
 
     @Override
-    public DeserializationConfig with(Locale l) {
-        return _withBase(_base.with(l));
-    }
-
-    @Override
-    public DeserializationConfig with(TimeZone tz) {
-        return _withBase(_base.with(tz));
-    }
-
-    @Override
-    public DeserializationConfig with(Base64Variant base64) {
-        return _withBase(_base.with(base64));
-    }
-
-    @Override
     public DeserializationConfig with(ContextAttributes attrs) {
         return (attrs == _attributes) ? this : new DeserializationConfig(this, attrs);
     }
-    
-    private final DeserializationConfig _withBase(BaseSettings newBase) {
-        return (_base == newBase) ? this : new DeserializationConfig(this, newBase);
-    }
 
     /*
     /**********************************************************
@@ -734,84 +631,6 @@
 
     /*
     /**********************************************************
-    /* MapperConfig implementation/overrides: introspection
-    /**********************************************************
-     */
-
-    /**
-     * Method for getting {@link AnnotationIntrospector} configured
-     * to introspect annotation values used for configuration.
-     */
-    @Override
-    public AnnotationIntrospector getAnnotationIntrospector()
-    {
-        /* 29-Jul-2009, tatu: it's now possible to disable use of
-         *   annotations; can be done using "no-op" introspector
-         */
-        if (isEnabled(MapperFeature.USE_ANNOTATIONS)) {
-            return super.getAnnotationIntrospector();
-        }
-        return NopAnnotationIntrospector.instance;
-    }
-
-    /**
-     * Accessor for getting bean description that only contains class
-     * annotations: useful if no getter/setter/creator information is needed.
-     */
-    @Override
-    public BeanDescription introspectClassAnnotations(JavaType type) {
-        return getClassIntrospector().forClassAnnotations(this, type, this);
-    }
-
-    /**
-     * Accessor for getting bean description that only contains immediate class
-     * annotations: ones from the class, and its direct mix-in, if any, but
-     * not from super types.
-     */
-    @Override
-    public BeanDescription introspectDirectClassAnnotations(JavaType type) {
-        return getClassIntrospector().forDirectClassAnnotations(this, type, this);
-    }
-
-    /*
-    /**********************************************************
-    /* Configuration: default settings with per-type overrides
-    /**********************************************************
-     */
-
-    @Override
-    public JsonInclude.Value getDefaultPropertyInclusion() {
-        return EMPTY_INCLUDE;
-    }
-
-    @Override
-    public JsonInclude.Value getDefaultPropertyInclusion(Class<?> baseType) {
-        ConfigOverride overrides = findConfigOverride(baseType);
-        if (overrides != null) {
-            JsonInclude.Value v = overrides.getInclude();
-            if (v != null) {
-                return v;
-            }
-        }
-        return EMPTY_INCLUDE;
-    }
-
-    @Override
-    public JsonInclude.Value getDefaultPropertyInclusion(Class<?> baseType,
-            JsonInclude.Value defaultIncl)
-    {
-        ConfigOverride overrides = findConfigOverride(baseType);
-        if (overrides != null) {
-            JsonInclude.Value v = overrides.getInclude();
-            if (v != null) {
-                return v;
-            }
-        }
-        return defaultIncl;
-    }
-
-    /*
-    /**********************************************************
     /* MapperConfig implementation/overrides: other
     /**********************************************************
      */
@@ -865,6 +684,18 @@
         return _deserFeatures;
     }
 
+    /**
+     * Convenience method equivalant to:
+     *<code>
+     *   isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)
+     *</code>
+     *
+     * @since 2.9
+     */
+    public final boolean requiresFullValue() {
+        return DeserializationFeature.FAIL_ON_TRAILING_TOKENS.enabledIn(_deserFeatures);
+    }
+
     /*
     /**********************************************************
     /* Other configuration
@@ -949,19 +780,6 @@
         } else {
             subtypes = getSubtypeResolver().collectAndResolveSubtypesByTypeId(this, ac);
         }
-        /* 04-May-2014, tatu: When called from DeserializerFactory, additional code like
-         *   this is invoked. But here we do not actually have access to mappings, so not
-         *   quite sure what to do, if anything. May need to revisit if the underlying
-         *   problem re-surfaces...
-         */
-        /*
-        if ((b.getDefaultImpl() == null) && baseType.isAbstract()) {
-            JavaType defaultType = mapAbstractType(config, baseType);
-            if (defaultType != null && defaultType.getRawClass() != baseType.getRawClass()) {
-                b = b.defaultImpl(defaultType.getRawClass());
-            }
-        }
-        */
         return b.buildTypeDeserializer(this, baseType, subtypes);
     }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/DeserializationContext.java b/src/main/java/com/fasterxml/jackson/databind/DeserializationContext.java
index 8ee553b..53f44a5 100644
--- a/src/main/java/com/fasterxml/jackson/databind/DeserializationContext.java
+++ b/src/main/java/com/fasterxml/jackson/databind/DeserializationContext.java
@@ -9,12 +9,16 @@
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.annotation.ObjectIdGenerator;
 import com.fasterxml.jackson.annotation.ObjectIdResolver;
+
 import com.fasterxml.jackson.core.*;
+
 import com.fasterxml.jackson.databind.cfg.ContextAttributes;
 import com.fasterxml.jackson.databind.deser.*;
 import com.fasterxml.jackson.databind.deser.impl.ObjectIdReader;
 import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId;
 import com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer;
+import com.fasterxml.jackson.databind.exc.MismatchedInputException;
+import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
 import com.fasterxml.jackson.databind.exc.InvalidFormatException;
 import com.fasterxml.jackson.databind.exc.InvalidTypeIdException;
 import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
@@ -31,14 +35,14 @@
  * Used to allow passing in configuration settings and reusable temporary
  * objects (scrap arrays, containers).
  *<p>
- * Instance life-cycle is such that an partially configured "blueprint" object
+ * Instance life-cycle is such that a partially configured "blueprint" object
  * is registered with {@link ObjectMapper} (and {@link ObjectReader},
- * and when an actual instance is needed for deserialization,
- * a fully configured instance will
- * be created using a method in excented API of sub-class
+ * and when actual instance is needed for deserialization,
+ * a fully configured instance will be created using a method in extended internal
+ *  API of sub-class
  * ({@link com.fasterxml.jackson.databind.deser.DefaultDeserializationContext#createInstance}).
  * Each instance is guaranteed to only be used from single-threaded context;
- * instances may be reused iff no configuration has changed.
+ * instances may be reused if (and only if) no configuration has changed.
  *<p>
  * Defined as abstract class so that implementations must define methods
  * for reconfiguring blueprints and creating instances.
@@ -49,13 +53,6 @@
 {
     private static final long serialVersionUID = 1L; // 2.6
 
-    /**
-     * Let's limit length of error messages, for cases where underlying data
-     * may be very large -- no point in spamming logs with megs of meaningless
-     * data.
-     */
-    private final static int MAX_ERROR_STR_LEN = 500;
-
     /*
     /**********************************************************
     /* Configuration, immutable
@@ -157,11 +154,13 @@
             DeserializerCache cache)
     {
         if (df == null) {
-            throw new IllegalArgumentException("Can not pass null DeserializerFactory");
+            throw new IllegalArgumentException("Cannot pass null DeserializerFactory");
         }
         _factory = df;
-        _cache = (cache == null) ? new DeserializerCache() : cache;
-        
+        if (cache == null) {
+            cache = new DeserializerCache();
+        }
+        _cache = cache;
         _featureFlags = 0;
         _config = null;
         _injectableValues = null;
@@ -374,9 +373,11 @@
 
     public final Object findInjectableValue(Object valueId,
             BeanProperty forProperty, Object beanInstance)
+        throws JsonMappingException
     {
         if (_injectableValues == null) {
-            throw new IllegalStateException("No 'injectableValues' configured, can not inject value with id ["+valueId+"]");
+            reportBadDefinition(ClassUtil.classOf(valueId), String.format(
+"No 'injectableValues' configured, cannot inject value with id [%s]", valueId));
         }
         return _injectableValues.findInjectableValue(valueId, this, forProperty, beanInstance);
     }
@@ -539,7 +540,7 @@
      * </pre>
      */
     public final JavaType constructType(Class<?> cls) {
-        return _config.constructType(cls);
+        return (cls == null) ? null : _config.constructType(cls);
     }
 
     /**
@@ -686,19 +687,6 @@
         return deser;
     }
 
-    @Deprecated // since 2.5; remove from 2.9
-    public JsonDeserializer<?> handlePrimaryContextualization(JsonDeserializer<?> deser, BeanProperty prop) throws JsonMappingException {
-        return handlePrimaryContextualization(deser, prop, TypeFactory.unknownType());
-    }
-
-    @Deprecated // since 2.5; remove from 2.9
-    public JsonDeserializer<?> handleSecondaryContextualization(JsonDeserializer<?> deser, BeanProperty prop) throws JsonMappingException {
-        if (deser instanceof ContextualDeserializer) {
-            deser = ((ContextualDeserializer) deser).createContextual(this, prop);
-        }
-        return deser;
-    }
-
     /*
     /**********************************************************
     /* Parsing methods that may use reusable/-cyclable objects
@@ -765,7 +753,8 @@
     public <T> T readValue(JsonParser p, JavaType type) throws IOException {
         JsonDeserializer<Object> deser = findRootValueDeserializer(type);
         if (deser == null) {
-            reportMappingException("Could not find JsonDeserializer for type %s", type);
+            reportBadDefinition(type,
+                    "Could not find JsonDeserializer for type "+type);
         }
         return (T) deser.deserialize(p, this);
     }
@@ -789,10 +778,9 @@
     public <T> T readPropertyValue(JsonParser p, BeanProperty prop, JavaType type) throws IOException {
         JsonDeserializer<Object> deser = findContextualValueDeserializer(type, prop);
         if (deser == null) {
-            String propName = (prop == null) ? "NULL" : ("'"+prop.getName()+"'");
-            reportMappingException(
+            return reportBadDefinition(type, String.format(
                     "Could not find JsonDeserializer for type %s (via property %s)",
-                    type, propName);
+                    type, ClassUtil.nameOf(prop)));
         }
         return (T) deser.deserialize(p, this);
     }
@@ -837,7 +825,7 @@
 
     /**
      * Method that deserializers should call if they encounter a String value
-     * that can not be converted to expected key of a {@link java.util.Map}
+     * that cannot be converted to expected key of a {@link java.util.Map}
      * valued property.
      * Default implementation will try to call {@link DeserializationProblemHandler#handleWeirdNumberValue}
      * on configured handlers, if any, to allow for recovery; if recovery does not
@@ -859,9 +847,7 @@
         throws IOException
     {
         // but if not handled, just throw exception
-        if (msgArgs.length > 0) {
-            msg = String.format(msg, msgArgs);
-        }
+        msg = _format(msg, msgArgs);
         LinkedNode<DeserializationProblemHandler> h = _config.getProblemHandlers();
         while (h != null) {
             // Can bail out if it's handled
@@ -882,7 +868,7 @@
 
     /**
      * Method that deserializers should call if they encounter a String value
-     * that can not be converted to target property type, in cases where some
+     * that cannot be converted to target property type, in cases where some
      * String values could be acceptable (either with different settings,
      * or different value).
      * Default implementation will try to call {@link DeserializationProblemHandler#handleWeirdStringValue}
@@ -905,9 +891,7 @@
         throws IOException
     {
         // but if not handled, just throw exception
-        if (msgArgs.length > 0) {
-            msg = String.format(msg, msgArgs);
-        }
+        msg = _format(msg, msgArgs);
         LinkedNode<DeserializationProblemHandler> h = _config.getProblemHandlers();
         while (h != null) {
             // Can bail out if it's handled
@@ -928,7 +912,7 @@
 
     /**
      * Method that deserializers should call if they encounter a numeric value
-     * that can not be converted to target property type, in cases where some
+     * that cannot be converted to target property type, in cases where some
      * numeric values could be acceptable (either with different settings,
      * or different numeric value).
      * Default implementation will try to call {@link DeserializationProblemHandler#handleWeirdNumberValue}
@@ -950,9 +934,7 @@
             String msg, Object... msgArgs)
         throws IOException
     {
-        if (msgArgs.length > 0) {
-            msg = String.format(msg, msgArgs);
-        }
+        msg = _format(msg, msgArgs);
         LinkedNode<DeserializationProblemHandler> h = _config.getProblemHandlers();
         while (h != null) {
             // Can bail out if it's handled
@@ -962,7 +944,7 @@
                 if (_isCompatible(targetClass, key)) {
                     return key;
                 }
-                throw weirdNumberException(value, targetClass, String.format(
+                throw weirdNumberException(value, targetClass, _format(
                         "DeserializationProblemHandler.handleWeirdNumberValue() for type %s returned value of type %s",
                         targetClass, key.getClass()));
             }
@@ -971,6 +953,28 @@
         throw weirdNumberException(value, targetClass, msg);
     }
 
+    public Object handleWeirdNativeValue(JavaType targetType, Object badValue,
+            JsonParser p)
+        throws IOException
+    {
+        LinkedNode<DeserializationProblemHandler> h = _config.getProblemHandlers();
+        final Class<?> raw = targetType.getRawClass();
+        for (; h != null; h = h.next()) {
+            // Can bail out if it's handled
+            Object goodValue = h.value().handleWeirdNativeValue(this, targetType, badValue, p);
+            if (goodValue != DeserializationProblemHandler.NOT_HANDLED) {
+                // Sanity check for broken handlers, otherwise nasty to debug:
+                if ((goodValue == null) || raw.isInstance(goodValue)) {
+                    return goodValue;
+                }
+                throw JsonMappingException.from(p, _format(
+"DeserializationProblemHandler.handleWeirdNativeValue() for type %s returned value of type %s",
+targetType, goodValue.getClass()));
+            }
+        }
+        throw weirdNativeValueException(badValue, raw);
+    }
+
     /**
      * Method that deserializers should call if they fail to instantiate value
      * due to lack of viable instantiator (usually creator, that is, constructor
@@ -980,36 +984,51 @@
      * just skipping it) to keep input state valid
      *
      * @param instClass Type that was to be instantiated
+     * @param valueInst (optional) Value instantiator to be used, if any; null if type does not
+     *    use one for instantiation (custom deserialiers don't; standard POJO deserializer does)
      * @param p Parser that points to the JSON value to decode
      *
      * @return Object that should be constructed, if any; has to be of type <code>instClass</code>
      *
-     * @since 2.8
+     * @since 2.9 (2.8 had alternate that did not take <code>ValueInstantiator</code>)
      */
-    public Object handleMissingInstantiator(Class<?> instClass, JsonParser p,
-            String msg, Object... msgArgs)
+    @SuppressWarnings("resource")
+    public Object handleMissingInstantiator(Class<?> instClass, ValueInstantiator valueInst,
+            JsonParser p, String msg, Object... msgArgs)
         throws IOException
     {
-        if (msgArgs.length > 0) {
-            msg = String.format(msg, msgArgs);
+        if (p == null) {
+            p = getParser();
         }
+        msg = _format(msg, msgArgs);
         LinkedNode<DeserializationProblemHandler> h = _config.getProblemHandlers();
         while (h != null) {
             // Can bail out if it's handled
             Object instance = h.value().handleMissingInstantiator(this,
-                    instClass, p, msg);
+                    instClass, valueInst, p, msg);
             if (instance != DeserializationProblemHandler.NOT_HANDLED) {
                 // Sanity check for broken handlers, otherwise nasty to debug:
                 if (_isCompatible(instClass, instance)) {
                     return instance;
                 }
-                throw instantiationException(instClass, String.format(
-                        "DeserializationProblemHandler.handleMissingInstantiator() for type %s returned value of type %s",
-                        instClass, instance.getClass()));
+                reportBadDefinition(constructType(instClass), String.format(
+"DeserializationProblemHandler.handleMissingInstantiator() for type %s returned value of type %s",
+                        instClass, ClassUtil.classNameOf(instance)));
             }
             h = h.next();
         }
-        throw instantiationException(instClass, msg);
+
+        // 16-Oct-2016, tatu: This is either a definition problem (if no applicable creator
+        //   exists), or input mismatch problem (otherwise) since none of existing creators
+        //   match with token.
+        if ((valueInst != null) && !valueInst.canInstantiate()) {
+            msg = String.format("Cannot construct instance of %s (no Creators, like default construct, exist): %s",
+                    ClassUtil.nameOf(instClass), msg);
+            return reportBadDefinition(constructType(instClass), msg);
+        }
+        msg = String.format("Cannot construct instance of %s (although at least one Creator exists): %s",
+                ClassUtil.nameOf(instClass), msg);
+        return reportInputMismatch(instClass, msg);
     }
 
     /**
@@ -1042,23 +1061,21 @@
                 if (_isCompatible(instClass, instance)) {
                     return instance;
                 }
-                throw instantiationException(instClass, String.format(
-                        "DeserializationProblemHandler.handleInstantiationProblem() for type %s returned value of type %s",
-                        instClass, instance.getClass()));
+                reportBadDefinition(constructType(instClass), String.format(
+"DeserializationProblemHandler.handleInstantiationProblem() for type %s returned value of type %s",
+                        instClass, ClassUtil.classNameOf(instance)));
             }
             h = h.next();
         }
         // 18-May-2016, tatu: Only wrap if not already a valid type to throw
-        if (t instanceof IOException) {
-            throw (IOException) t;
-        }
+        ClassUtil.throwIfIOE(t);
         throw instantiationException(instClass, t);
     }
 
     /**
      * Method that deserializers should call if the first token of the value to
      * deserialize is of unexpected type (that is, type of token that deserializer
-     * can not handle). This could occur, for example, if a Number deserializer
+     * cannot handle). This could occur, for example, if a Number deserializer
      * encounter {@link JsonToken#START_ARRAY} instead of
      * {@link JsonToken#VALUE_NUMBER_INT} or {@link JsonToken#VALUE_NUMBER_FLOAT}.
      * 
@@ -1074,15 +1091,16 @@
     {
         return handleUnexpectedToken(instClass, p.getCurrentToken(), p, null);
     }
-    
+
     /**
      * Method that deserializers should call if the first token of the value to
      * deserialize is of unexpected type (that is, type of token that deserializer
-     * can not handle). This could occur, for example, if a Number deserializer
+     * cannot handle). This could occur, for example, if a Number deserializer
      * encounter {@link JsonToken#START_ARRAY} instead of
      * {@link JsonToken#VALUE_NUMBER_INT} or {@link JsonToken#VALUE_NUMBER_FLOAT}.
      * 
      * @param instClass Type that was to be instantiated
+     * @param t Token encountered that does match expected
      * @param p Parser that points to the JSON value to decode
      *
      * @return Object that should be constructed, if any; has to be of type <code>instClass</code>
@@ -1090,13 +1108,10 @@
      * @since 2.8
      */
     public Object handleUnexpectedToken(Class<?> instClass, JsonToken t,
-            JsonParser p,
-            String msg, Object... msgArgs)
+            JsonParser p, String msg, Object... msgArgs)
         throws IOException
     {
-        if (msgArgs.length > 0) {
-            msg = String.format(msg, msgArgs);
-        }
+        msg = _format(msg, msgArgs);
         LinkedNode<DeserializationProblemHandler> h = _config.getProblemHandlers();
         while (h != null) {
             Object instance = h.value().handleUnexpectedToken(this,
@@ -1105,31 +1120,32 @@
                 if (_isCompatible(instClass, instance)) {
                     return instance;
                 }
-                reportMappingException("DeserializationProblemHandler.handleUnexpectedToken() for type %s returned value of type %s",
-                        instClass, instance.getClass());
+                reportBadDefinition(constructType(instClass), String.format(
+                        "DeserializationProblemHandler.handleUnexpectedToken() for type %s returned value of type %s",
+                        ClassUtil.nameOf(instClass), ClassUtil.classNameOf(instance)));
             }
             h = h.next();
         }
         if (msg == null) {
             if (t == null) {
                 msg = String.format("Unexpected end-of-input when binding data into %s",
-                        _calcName(instClass));
+                        ClassUtil.nameOf(instClass));
             } else {
-                msg = String.format("Can not deserialize instance of %s out of %s token",
-                        _calcName(instClass), t);
+                msg = String.format("Cannot deserialize instance of %s out of %s token",
+                        ClassUtil.nameOf(instClass), t);
             }
         }
-        reportMappingException(msg);
+        reportInputMismatch(instClass, msg);
         return null; // never gets here
     }
 
     /**
      * Method that deserializers should call if they encounter a type id
-     * (for polymorphic deserialization) that can not be resolved to an
+     * (for polymorphic deserialization) that cannot be resolved to an
      * actual type; usually since there is no mapping defined.
      * Default implementation will try to call {@link DeserializationProblemHandler#handleUnknownTypeId}
      * on configured handlers, if any, to allow for recovery; if recovery does not
-     * succeed, will throw exception constructed with {@link #unknownTypeIdException}.
+     * succeed, will throw exception constructed with {@link #invalidTypeIdException}.
      *
      * @param baseType Base type from which resolution starts
      * @param id Type id that could not be converted
@@ -1138,7 +1154,7 @@
      *
      * @return {@link JavaType} that id resolves to
      *
-     * @throws IOException To indicate unrecoverable problem, if resolution can not
+     * @throws IOException To indicate unrecoverable problem, if resolution cannot
      *    be made to work
      *
      * @since 2.8
@@ -1158,7 +1174,7 @@
                 if (type.isTypeOrSubTypeOf(baseType.getRawClass())) {
                     return type;
                 }
-                throw unknownTypeIdException(baseType, id,
+                throw invalidTypeIdException(baseType, id,
                         "problem handler tried to resolve into non-subtype: "+type);
             }
             h = h.next();
@@ -1167,7 +1183,38 @@
         if (!isEnabled(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE)) {
             return null;
         }
-        throw unknownTypeIdException(baseType, id, extraDesc);
+        throw invalidTypeIdException(baseType, id, extraDesc);
+    }
+
+    /**
+     * @since 2.9
+     */
+    public JavaType handleMissingTypeId(JavaType baseType,
+            TypeIdResolver idResolver, String extraDesc) throws IOException
+    {
+        LinkedNode<DeserializationProblemHandler> h = _config.getProblemHandlers();
+        while (h != null) {
+            // Can bail out if it's handled
+            JavaType type = h.value().handleMissingTypeId(this, baseType, idResolver, extraDesc);
+            if (type != null) {
+                if (type.hasRawClass(Void.class)) {
+                    return null;
+                }
+                // But ensure there's type compatibility
+                if (type.isTypeOrSubTypeOf(baseType.getRawClass())) {
+                    return type;
+                }
+                throw invalidTypeIdException(baseType, null,
+                        "problem handler tried to resolve into non-subtype: "+type);
+            }
+            h = h.next();
+        }
+        // 09-Mar-2017, tatu: We may want to consider yet another feature at some
+        //    point to allow returning `null`... but that seems bit risky for now
+//        if (!isEnabled(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE)) {
+//            return null;
+//        }
+        throw missingTypeIdException(baseType, extraDesc);
     }
 
     /**
@@ -1186,7 +1233,7 @@
     /*
     /**********************************************************
     /* Methods for problem reporting, in cases where recovery
-    /* is not considered possible
+    /* is not considered possible: input problem
     /**********************************************************
      */
 
@@ -1198,15 +1245,131 @@
      * recovery is attempted (via {@link DeserializationProblemHandler}, as
      * problem is considered to be difficult to recover from, in general.
      * 
+     * @since 2.9
+     */
+    public void reportWrongTokenException(JsonDeserializer<?> deser,
+            JsonToken expToken, String msg, Object... msgArgs)
+        throws JsonMappingException
+    {
+        msg = _format(msg, msgArgs);
+        throw wrongTokenException(getParser(), deser.handledType(), expToken, msg);
+    }
+    
+    /**
+     * Method for deserializers to call 
+     * when the token encountered was of type different than what <b>should</b>
+     * be seen at that position, usually within a sequence of expected tokens.
+     * Note that this method will throw a {@link JsonMappingException} and no
+     * recovery is attempted (via {@link DeserializationProblemHandler}, as
+     * problem is considered to be difficult to recover from, in general.
+     * 
+     * @since 2.9
+     */
+    public void reportWrongTokenException(JavaType targetType,
+            JsonToken expToken, String msg, Object... msgArgs)
+        throws JsonMappingException
+    {
+        msg = _format(msg, msgArgs);
+        throw wrongTokenException(getParser(), targetType, expToken, msg);
+    }
+
+    /**
+     * Method for deserializers to call 
+     * when the token encountered was of type different than what <b>should</b>
+     * be seen at that position, usually within a sequence of expected tokens.
+     * Note that this method will throw a {@link JsonMappingException} and no
+     * recovery is attempted (via {@link DeserializationProblemHandler}, as
+     * problem is considered to be difficult to recover from, in general.
+     * 
+     * @since 2.9
+     */
+    public void reportWrongTokenException(Class<?> targetType,
+            JsonToken expToken, String msg, Object... msgArgs)
+        throws JsonMappingException
+    {
+        msg = _format(msg, msgArgs);
+        throw wrongTokenException(getParser(), targetType, expToken, msg);
+    }
+
+    /**
      * @since 2.8
      */
+    public <T> T reportUnresolvedObjectId(ObjectIdReader oidReader, Object bean)
+        throws JsonMappingException
+    {
+        String msg = String.format("No Object Id found for an instance of %s, to assign to property '%s'",
+                ClassUtil.classNameOf(bean), oidReader.propertyName);
+        return reportInputMismatch(oidReader.idProperty, msg);
+    }
+
+    /**
+     * Helper method used to indicate a problem with input in cases where more
+     * specific <code>reportXxx()</code> method was not available.
+     *
+     * @since 2.9
+     */
+    public <T> T reportInputMismatch(BeanProperty prop,
+            String msg, Object... msgArgs) throws JsonMappingException
+    {
+        msg = _format(msg, msgArgs);
+        JavaType type = (prop == null) ? null : prop.getType();
+        throw MismatchedInputException.from(getParser(), type, msg);
+    }
+
+    /**
+     * Helper method used to indicate a problem with input in cases where more
+     * specific <code>reportXxx()</code> method was not available.
+     *
+     * @since 2.9
+     */
+    public <T> T reportInputMismatch(JsonDeserializer<?> src,
+            String msg, Object... msgArgs) throws JsonMappingException
+    {
+        msg = _format(msg, msgArgs);
+        throw MismatchedInputException.from(getParser(), src.handledType(), msg);
+    }
+
+    /**
+     * Helper method used to indicate a problem with input in cases where more
+     * specific <code>reportXxx()</code> method was not available.
+     *
+     * @since 2.9
+     */
+    public <T> T reportInputMismatch(Class<?> targetType,
+            String msg, Object... msgArgs) throws JsonMappingException
+    {
+        msg = _format(msg, msgArgs);
+        throw MismatchedInputException.from(getParser(), targetType, msg);
+    }
+
+    /**
+     * Helper method used to indicate a problem with input in cases where more
+     * specific <code>reportXxx()</code> method was not available.
+     *
+     * @since 2.9
+     */
+    public <T> T reportInputMismatch(JavaType targetType,
+            String msg, Object... msgArgs) throws JsonMappingException
+    {
+        msg = _format(msg, msgArgs);
+        throw MismatchedInputException.from(getParser(), targetType, msg);
+    }
+
+    public <T> T reportTrailingTokens(Class<?> targetType,
+            JsonParser p, JsonToken trailingToken) throws JsonMappingException
+    {
+        throw MismatchedInputException.from(p, targetType, String.format(
+"Trailing token (of type %s) found after value (bound as %s): not allowed as per `DeserializationFeature.FAIL_ON_TRAILING_TOKENS`",
+trailingToken, ClassUtil.nameOf(targetType)
+                ));
+    }
+
+    @Deprecated // since 2.9
     public void reportWrongTokenException(JsonParser p,
             JsonToken expToken, String msg, Object... msgArgs)
         throws JsonMappingException
     {
-        if ((msg != null) && (msgArgs.length > 0)) {
-            msg = String.format(msg, msgArgs);
-        }
+        msg = _format(msg, msgArgs);
         throw wrongTokenException(p, expToken, msg);
     }
     
@@ -1226,52 +1389,31 @@
             JsonDeserializer<?> deser)
         throws JsonMappingException
     {
-        if (!isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)) {
-            return;
+        if (isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)) {
+            // Do we know properties that are expected instead?
+            Collection<Object> propIds = (deser == null) ? null : deser.getKnownPropertyNames();
+            throw UnrecognizedPropertyException.from(_parser,
+                    instanceOrClass, fieldName, propIds);
         }
-        // Do we know properties that are expected instead?
-        Collection<Object> propIds = (deser == null) ? null : deser.getKnownPropertyNames();
-        throw UnrecognizedPropertyException.from(_parser,
-                instanceOrClass, fieldName, propIds);
     }
 
     /**
      * @since 2.8
+     *
+     * @deprecated Since 2.9: not clear this ever occurs
      */
-    public void reportMappingException(String msg, Object... msgArgs)
-        throws JsonMappingException
-    {
-        if (msgArgs.length > 0) {
-            msg = String.format(msg, msgArgs);
-        }
-        throw JsonMappingException.from(getParser(), msg);
+    @Deprecated // since 2.9
+    public void reportMissingContent(String msg, Object... msgArgs) throws JsonMappingException {
+        throw MismatchedInputException.from(getParser(), (JavaType) null, "No content to map due to end-of-input");
     }
 
-    /**
-     * @since 2.8
+    /*
+    /**********************************************************
+    /* Methods for problem reporting, in cases where recovery
+    /* is not considered possible: POJO definition problems
+    /**********************************************************
      */
-    public void reportMissingContent(String msg, Object... msgArgs)
-        throws JsonMappingException
-    {
-        if (msg == null) {
-            msg = "No content to map due to end-of-input";
-        } else if (msgArgs.length > 0) {
-            msg = String.format(msg, msgArgs);
-        }
-        throw JsonMappingException.from(getParser(), msg);
-    }
-
-    /**
-     * @since 2.8
-     */
-    public void reportUnresolvedObjectId(ObjectIdReader oidReader, Object bean)
-        throws JsonMappingException
-    {
-        String msg = String.format("No Object Id found for an instance of %s, to assign to property '%s'",
-                bean.getClass().getName(), oidReader.propertyName);
-        throw JsonMappingException.from(getParser(), msg);
-    }
-
+    
     /**
      * Helper method called to indicate problem in POJO (serialization) definitions or settings
      * regarding specific Java type, unrelated to actual JSON content to map.
@@ -1280,13 +1422,11 @@
      * @since 2.9
      */
     public <T> T reportBadTypeDefinition(BeanDescription bean,
-            String message, Object... args) throws JsonMappingException {
-        if (args != null && args.length > 0) {
-            message = String.format(message, args);
-        }
-        String beanDesc = (bean == null) ? "N/A" : _desc(bean.getType().getGenericSignature());
-        throw mappingException("Invalid type definition for type %s: %s",
-                beanDesc, message);
+            String msg, Object... msgArgs) throws JsonMappingException {
+        msg = _format(msg, msgArgs);
+        String beanDesc = ClassUtil.nameOf(bean.getBeanClass());
+        msg = String.format("Invalid type definition for type %s: %s", beanDesc, msg);
+        throw InvalidDefinitionException.from(_parser, msg, bean, null);
     }
 
     /**
@@ -1297,70 +1437,39 @@
      * @since 2.9
      */
     public <T> T reportBadPropertyDefinition(BeanDescription bean, BeanPropertyDefinition prop,
-            String message, Object... args) throws JsonMappingException {
-        if (args != null && args.length > 0) {
-            message = String.format(message, args);
+            String msg, Object... msgArgs) throws JsonMappingException {
+        msg = _format(msg, msgArgs);
+        String propName = ClassUtil.nameOf(prop);
+        String beanDesc = ClassUtil.nameOf(bean.getBeanClass());
+        msg = String.format("Invalid definition for property %s (of type %s): %s",
+                propName, beanDesc, msg);
+        throw InvalidDefinitionException.from(_parser, msg, bean, prop);
+    }
+
+    @Override
+    public <T> T reportBadDefinition(JavaType type, String msg) throws JsonMappingException {
+        throw InvalidDefinitionException.from(_parser, msg, type);
+    }
+
+    /**
+     * Method that deserializer may call if it is called to do an update ("merge")
+     * but deserializer operates on a non-mergeable type. Although this should
+     * usually be caught earlier, sometimes it may only be caught during operation
+     * and if so this is the method to call.
+     * Note that if {@link MapperFeature#IGNORE_MERGE_FOR_UNMERGEABLE} is enabled,
+     * this method will simply return null; otherwise {@link InvalidDefinitionException}
+     * will be thrown.
+     *
+     * @since 2.9
+     */
+    public <T> T reportBadMerge(JsonDeserializer<?> deser) throws JsonMappingException
+    {
+        if (isEnabled(MapperFeature.IGNORE_MERGE_FOR_UNMERGEABLE)) {
+            return null;
         }
-        String propName = (prop == null)  ? "N/A" : _quotedString(prop.getName());
-        String beanDesc = (bean == null) ? "N/A" : _desc(bean.getType().getGenericSignature());
-        throw mappingException("Invalid definition for property %s (of type %s): %s",
-                propName, beanDesc, message);
-    }
-
-    /*
-    /**********************************************************
-    /* Methods for constructing exceptions, "untyped"
-    /**********************************************************
-     */
-
-    /**
-     * Helper method for constructing generic mapping exception with specified
-     * message and current location information.
-     * Note that application code should almost always call
-     * one of <code>handleXxx</code> methods, or {@link #reportMappingException(String, Object...)}
-     * instead.
-     * 
-     * @since 2.6
-     */
-    public JsonMappingException mappingException(String message) {
-        return JsonMappingException.from(getParser(), message);
-    }
-
-    /**
-     * Helper method for constructing generic mapping exception with specified
-     * message and current location information
-     * Note that application code should almost always call
-     * one of <code>handleXxx</code> methods, or {@link #reportMappingException(String, Object...)}
-     * instead.
-     * 
-     * @since 2.6
-     */
-    public JsonMappingException mappingException(String msgTemplate, Object... args) {
-        if (args != null && args.length > 0) {
-            msgTemplate = String.format(msgTemplate, args);
-        }
-        return JsonMappingException.from(getParser(), msgTemplate);
-    }
-
-    /**
-     * Helper method for constructing generic mapping exception for specified type
-     * 
-     * @deprecated Since 2.8 use {@link #handleUnexpectedToken(Class, JsonParser)} instead
-     */
-    @Deprecated
-    public JsonMappingException mappingException(Class<?> targetClass) {
-        return mappingException(targetClass, _parser.getCurrentToken());
-    }
-
-    /**
-     * @deprecated Since 2.8 use {@link #handleUnexpectedToken(Class, JsonParser)} instead
-     */
-    @Deprecated
-    public JsonMappingException mappingException(Class<?> targetClass, JsonToken token) {
-        String tokenDesc = (token == null) ? "<end of input>" : String.format("%s token", token);
-        return JsonMappingException.from(_parser,
-                String.format("Can not deserialize instance of %s out of %s",
-                        _calcName(targetClass), tokenDesc));
+        JavaType type = constructType(deser.handledType());
+        String msg = String.format("Invalid configuration: values of type %s cannot be merged", type);
+        throw InvalidDefinitionException.from(getParser(), msg, type);
     }
 
     /*
@@ -1377,16 +1486,32 @@
      * Note that most of the time this method should NOT be directly called;
      * instead, {@link #reportWrongTokenException} should be called and will
      * call this method as necessary.
+     *
+     * @since 2.9
      */
-    public JsonMappingException wrongTokenException(JsonParser p, JsonToken expToken,
-            String msg0)
+    public JsonMappingException wrongTokenException(JsonParser p, JavaType targetType,
+            JsonToken expToken, String extra)
     {
         String msg = String.format("Unexpected token (%s), expected %s",
                 p.getCurrentToken(), expToken);
-        if (msg0 != null) {
-            msg = msg + ": "+msg0;
-        }
-        return JsonMappingException.from(p, msg);
+        msg = _colonConcat(msg, extra);
+        return MismatchedInputException.from(p, targetType, msg);
+    }
+
+    public JsonMappingException wrongTokenException(JsonParser p, Class<?> targetType,
+            JsonToken expToken, String extra)
+    {
+        String msg = String.format("Unexpected token (%s), expected %s",
+                p.getCurrentToken(), expToken);
+        msg = _colonConcat(msg, extra);
+        return MismatchedInputException.from(p, targetType, msg);
+    }
+    
+    @Deprecated // since 2.9
+    public JsonMappingException wrongTokenException(JsonParser p, JsonToken expToken,
+            String msg)
+    {
+        return wrongTokenException(p, (JavaType) null, expToken, msg);
     }
 
     /**
@@ -1400,8 +1525,8 @@
     public JsonMappingException weirdKeyException(Class<?> keyClass, String keyValue,
             String msg) {
         return InvalidFormatException.from(_parser,
-                String.format("Can not deserialize Map key of type %s from String %s: %s",
-                        keyClass.getName(), _quotedString(keyValue), msg),
+                String.format("Cannot deserialize Map key of type %s from String %s: %s",
+                        ClassUtil.nameOf(keyClass), _quotedString(keyValue), msg),
                 keyValue, keyClass);
     }
 
@@ -1421,8 +1546,8 @@
     public JsonMappingException weirdStringException(String value, Class<?> instClass,
             String msg) {
         return InvalidFormatException.from(_parser,
-                String.format("Can not deserialize value of type %s from String %s: %s",
-                        instClass.getName(), _quotedString(value), msg),
+                String.format("Cannot deserialize value of type %s from String %s: %s",
+                        ClassUtil.nameOf(instClass), _quotedString(value), msg),
                 value, instClass);
     }
 
@@ -1436,8 +1561,26 @@
     public JsonMappingException weirdNumberException(Number value, Class<?> instClass,
             String msg) {
         return InvalidFormatException.from(_parser,
-                String.format("Can not deserialize value of type %s from number %s: %s",
-                        instClass.getName(), String.valueOf(value), msg),
+                String.format("Cannot deserialize value of type %s from number %s: %s",
+                        ClassUtil.nameOf(instClass), String.valueOf(value), msg),
+                value, instClass);
+    }
+
+    /**
+     * Helper method for constructing exception to indicate that input JSON
+     * token of type "native value" (see {@link JsonToken#VALUE_EMBEDDED_OBJECT})
+     * is of incompatible type (and there is no delegating creator or such to use)
+     * and can not be used to construct value of specified type (usually POJO).
+     * Note that most of the time this method should NOT be called; instead,
+     * {@link #handleWeirdNativeValue} should be called which will call this method
+     *
+     * @since 2.9
+     */
+    public JsonMappingException weirdNativeValueException(Object value, Class<?> instClass)
+    {
+        return InvalidFormatException.from(_parser, String.format(
+"Cannot deserialize value of type %s from native value (`JsonToken.VALUE_EMBEDDED_OBJECT`) of type %s: incompatible types",
+            ClassUtil.nameOf(instClass), ClassUtil.classNameOf(value)),
                 value, instClass);
     }
 
@@ -1450,10 +1593,14 @@
      * {@link #handleInstantiationProblem} should be called which will call this method
      * if necessary.
      */
-    public JsonMappingException instantiationException(Class<?> instClass, Throwable t) {
-        return JsonMappingException.from(_parser,
-                String.format("Can not construct instance of %s, problem: %s",
-                        instClass.getName(), t.getMessage()), t);
+    public JsonMappingException instantiationException(Class<?> instClass, Throwable cause) {
+        // Most likely problem with Creator definition, right?
+        JavaType type = constructType(instClass);
+        String msg = String.format("Cannot construct instance of %s, problem: %s",
+                ClassUtil.nameOf(instClass), cause.getMessage());
+        InvalidDefinitionException e = InvalidDefinitionException.from(_parser, msg, type);
+        e.initCause(cause);
+        return e;
     }
 
     /**
@@ -1465,29 +1612,30 @@
      * {@link #handleMissingInstantiator} should be called which will call this method
      * if necessary.
      */
-    public JsonMappingException instantiationException(Class<?> instClass, String msg) {
-        return JsonMappingException.from(_parser,
-                String.format("Can not construct instance of %s: %s",
-                        instClass.getName(), msg));
+    public JsonMappingException instantiationException(Class<?> instClass, String msg0) {
+        // Most likely problem with Creator definition, right?
+        JavaType type = constructType(instClass);
+        String msg = String.format("Cannot construct instance of %s: %s",
+                ClassUtil.nameOf(instClass), msg0);
+        return InvalidDefinitionException.from(_parser, msg, type);
+    }
+
+    @Override
+    public JsonMappingException invalidTypeIdException(JavaType baseType, String typeId,
+            String extraDesc) {
+        String msg = String.format("Could not resolve type id '%s' as a subtype of %s",
+                typeId, baseType);
+        return InvalidTypeIdException.from(_parser, _colonConcat(msg, extraDesc), baseType, typeId);
     }
 
     /**
-     * Helper method for constructing exception to indicate that given type id
-     * could not be resolved to a valid subtype of specified base type, during
-     * polymorphic deserialization.
-     *<p>
-     * Note that most of the time this method should NOT be called; instead,
-     * {@link #handleUnknownTypeId} should be called which will call this method
-     * if necessary.
+     * @since 2.9
      */
-    public JsonMappingException unknownTypeIdException(JavaType baseType, String typeId,
+    public JsonMappingException missingTypeIdException(JavaType baseType,
             String extraDesc) {
-        String msg = String.format("Could not resolve type id '%s' into a subtype of %s",
-                typeId, baseType);
-        if (extraDesc != null) {
-            msg = msg + ": "+extraDesc;
-        }
-        return InvalidTypeIdException.from(_parser, msg, baseType, typeId);
+        String msg = String.format("Missing type id when trying to resolve subtype of %s",
+                baseType);
+        return InvalidTypeIdException.from(_parser, _colonConcat(msg, extraDesc), baseType, null);
     }
 
     /*
@@ -1503,13 +1651,12 @@
      */
     @Deprecated
     public JsonMappingException unknownTypeException(JavaType type, String id,
-            String extraDesc) {
+            String extraDesc)
+    {
         String msg = String.format("Could not resolve type id '%s' into a subtype of %s",
                 id, type);
-        if (extraDesc != null) {
-            msg = msg + ": "+extraDesc;
-        }
-        return JsonMappingException.from(_parser, msg);
+        msg = _colonConcat(msg, extraDesc);
+        return MismatchedInputException.from(_parser, type, msg);
     }
 
     /**
@@ -1520,8 +1667,84 @@
      */
     @Deprecated
     public JsonMappingException endOfInputException(Class<?> instClass) {
-        return JsonMappingException.from(_parser, "Unexpected end-of-input when trying to deserialize a "
-                +instClass.getName());
+        return MismatchedInputException.from(_parser, instClass,
+                "Unexpected end-of-input when trying to deserialize a "+instClass.getName());
+    }
+
+    /*
+    /**********************************************************
+    /* Deprecated methods for constructing, throwing non-specific
+    /* JsonMappingExceptions: as of 2.9, should use more specific
+    /* ones.
+    /**********************************************************
+     */
+    
+    /**
+     * Fallback method that may be called if no other <code>reportXxx</code>
+     * is applicable -- but only in that case.
+     *
+     * @since 2.8
+     * 
+     * @deprecated Since 2.9: use a more specific method, or {@link #reportBadDefinition(JavaType, String)},
+     *    or {@link #reportInputMismatch} instead
+     */
+    @Deprecated // since 2.9
+    public void reportMappingException(String msg, Object... msgArgs)
+        throws JsonMappingException
+    {
+        throw JsonMappingException.from(getParser(), _format(msg, msgArgs));
+    }
+
+    /**
+     * Helper method for constructing generic mapping exception with specified
+     * message and current location information.
+     * Note that application code should almost always call
+     * one of <code>handleXxx</code> methods, or {@link #reportMappingException(String, Object...)}
+     * instead.
+     * 
+     * @since 2.6
+     * 
+     * @deprecated Since 2.9 use more specific error reporting methods instead
+     */
+    @Deprecated
+    public JsonMappingException mappingException(String message) {
+        return JsonMappingException.from(getParser(), message);
+    }
+
+    /**
+     * Helper method for constructing generic mapping exception with specified
+     * message and current location information
+     * Note that application code should almost always call
+     * one of <code>handleXxx</code> methods, or {@link #reportMappingException(String, Object...)}
+     * instead.
+     * 
+     * @since 2.6
+     *
+     * @deprecated Since 2.9 use more specific error reporting methods instead
+     */
+    @Deprecated
+    public JsonMappingException mappingException(String msg, Object... msgArgs) {
+        return JsonMappingException.from(getParser(), _format(msg, msgArgs));
+    }
+
+    /**
+     * Helper method for constructing generic mapping exception for specified type
+     * 
+     * @deprecated Since 2.8 use {@link #handleUnexpectedToken(Class, JsonParser)} instead
+     */
+    @Deprecated
+    public JsonMappingException mappingException(Class<?> targetClass) {
+        return mappingException(targetClass, _parser.getCurrentToken());
+    }
+
+    /**
+     * @deprecated Since 2.8 use {@link #handleUnexpectedToken(Class, JsonParser)} instead
+     */
+    @Deprecated
+    public JsonMappingException mappingException(Class<?> targetClass, JsonToken token) {
+        return JsonMappingException.from(_parser,
+                String.format("Cannot deserialize instance of %s out of %s token",
+                        ClassUtil.nameOf(targetClass), token));
     }
 
     /*
@@ -1544,48 +1767,4 @@
         _dateFormat = df = (DateFormat) df.clone();
         return df;
     }
-
-    protected String determineClassName(Object instance) {
-        return ClassUtil.getClassDescription(instance);
-    }
-
-    protected String _calcName(Class<?> cls) {
-        if (cls.isArray()) {
-            return _calcName(cls.getComponentType())+"[]";
-        }
-        return cls.getName();
-    }
-
-    protected String _valueDesc() {
-        try {
-            return _desc(_parser.getText());
-        } catch (Exception e) {
-            return "[N/A]";
-        }
-    }
-
-    protected String _desc(String desc) {
-        if (desc == null) {
-            return "[N/A]";
-        }
-        // !!! should we quote it? (in case there are control chars, linefeeds)
-        if (desc.length() > MAX_ERROR_STR_LEN) {
-            desc = desc.substring(0, MAX_ERROR_STR_LEN) + "]...[" + desc.substring(desc.length() - MAX_ERROR_STR_LEN);
-        }
-        return desc;
-    }
-
-    // @since 2.7
-    protected String _quotedString(String desc) {
-        if (desc == null) {
-            return "[N/A]";
-        }
-        // !!! should we quote it? (in case there are control chars, linefeeds)
-        if (desc.length() > MAX_ERROR_STR_LEN) {
-            return String.format("\"%s]...[%s\"",
-                    desc.substring(0, MAX_ERROR_STR_LEN),
-                    desc.substring(desc.length() - MAX_ERROR_STR_LEN));
-        }
-        return "\"" + desc + "\"";
-    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/DeserializationFeature.java b/src/main/java/com/fasterxml/jackson/databind/DeserializationFeature.java
index 1505419..5fd5ca4 100644
--- a/src/main/java/com/fasterxml/jackson/databind/DeserializationFeature.java
+++ b/src/main/java/com/fasterxml/jackson/databind/DeserializationFeature.java
@@ -21,7 +21,7 @@
 {
     /*
     /******************************************************
-    /* Type conversion features
+    /* Value (mostly scalar) conversion features
     /******************************************************
      */
 
@@ -92,23 +92,10 @@
      * {@link java.util.List}s.
      */
     USE_JAVA_ARRAY_FOR_JSON_ARRAY(false),
-    
-    /**
-     * Feature that determines standard deserialization mechanism used for
-     * Enum values: if enabled, Enums are assumed to have been serialized  using
-     * return value of <code>Enum.toString()</code>;
-     * if disabled, return value of <code>Enum.name()</code> is assumed to have been used.
-     *<p>
-     * Note: this feature should usually have same value
-     * as {@link SerializationFeature#WRITE_ENUMS_USING_TO_STRING}.
-     *<p>
-     * Feature is disabled by default.
-     */
-    READ_ENUMS_USING_TO_STRING(false),
-    
+
     /*
     /******************************************************
-     *  Error handling features
+    /* Error handling features
     /******************************************************
      */
 
@@ -156,7 +143,7 @@
     /**
      * Feature that determines what happens when type of a polymorphic
      * value (indicated for example by {@link com.fasterxml.jackson.annotation.JsonTypeInfo})
-     * can not be found (missing) or resolved (invalid class name, unmappable id);
+     * cannot be found (missing) or resolved (invalid class name, unmappable id);
      * if enabled, an exception ir thrown; if false, null value is used instead.
      *<p>
      * Feature is enabled by default so that exception is thrown for missing or invalid
@@ -259,6 +246,26 @@
     FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY(true),
 
     /**
+     * Feature that determines behaviour for data-binding after binding the root value.
+     * If feature is enabled, one more call to
+     * {@link com.fasterxml.jackson.core.JsonParser#nextToken} is made to ensure that
+     * no more tokens are found (and if any is found,
+     * {@link com.fasterxml.jackson.databind.exc.MismatchedInputException} is thrown); if
+     * disabled, no further checks are made.
+     *<p>
+     * Feature could alternatively be called <code>READ_FULL_STREAM</code>, since it
+     * effectively verifies that input stream contains only as much data as is needed
+     * for binding the full value, and nothing more (except for possible ignorable
+     * white space or comments, if supported by data format).
+     *<p>
+     * Feature is disabled by default (so that no check is made for possible trailing
+     * token(s)) for backwards compatibility reasons.
+     *
+     * @since 2.9
+     */
+    FAIL_ON_TRAILING_TOKENS(false),
+    
+    /**
      * Feature that determines whether Jackson code should catch
      * and wrap {@link Exception}s (but never {@link Error}s!)
      * to add additional information about
@@ -325,21 +332,27 @@
     
     /**
      * Feature that can be enabled to allow JSON empty String
-     * value ("") to be bound to POJOs as null.
-     * If disabled, standard POJOs can only be bound from JSON null or
+     * value ("") to be bound as `null` for POJOs and other structured
+     * values ({@link java.util.Map}s, {@link java.util.Collection}s).
+     * If disabled, standard POJOs can only be bound from JSON `null` or
      * JSON Object (standard meaning that no custom deserializers or
      * constructors are defined; both of which can add support for other
      * kinds of JSON values); if enabled, empty JSON String can be taken
      * to be equivalent of JSON null.
      *<p>
+     * NOTE: this does NOT apply to scalar values such as booleans and numbers;
+     * whether they can be coerced depends on
+     * {@link MapperFeature#ALLOW_COERCION_OF_SCALARS}.
+     *<p>
      * Feature is disabled by default.
      */
     ACCEPT_EMPTY_STRING_AS_NULL_OBJECT(false),
 
     /**
      * Feature that can be enabled to allow empty JSON Array
-     * value (that is, <code>[ ]</code>) to be bound to POJOs as null.
-     * If disabled, standard POJOs can only be bound from JSON null or
+     * value (that is, <code>[ ]</code>) to be bound to POJOs (and
+     * with 2.9, other values too) as `null`.
+     * If disabled, standard POJOs can only be bound from JSON `null` or
      * JSON Object (standard meaning that no custom deserializers or
      * constructors are defined; both of which can add support for other
      * kinds of JSON values); if enabled, empty JSON Array will be taken
@@ -364,7 +377,20 @@
      * @since 2.6
      */
     ACCEPT_FLOAT_AS_INT(true),
-    
+
+    /**
+     * Feature that determines standard deserialization mechanism used for
+     * Enum values: if enabled, Enums are assumed to have been serialized  using
+     * return value of <code>Enum.toString()</code>;
+     * if disabled, return value of <code>Enum.name()</code> is assumed to have been used.
+     *<p>
+     * Note: this feature should usually have same value
+     * as {@link SerializationFeature#WRITE_ENUMS_USING_TO_STRING}.
+     *<p>
+     * Feature is disabled by default.
+     */
+    READ_ENUMS_USING_TO_STRING(false),
+
     /**
      * Feature that allows unknown Enum values to be parsed as null values. 
      * If disabled, unknown Enum values will throw exceptions.
@@ -420,9 +446,14 @@
      * Note that exact behavior depends on date/time types in question; and specifically
      * JDK type of {@link java.util.Date} does NOT have in-built timezone information
      * so this setting has no effect.
+     * Further, while {@link java.util.Calendar} does have this information basic
+     * JDK {@link java.text.SimpleDateFormat} is unable to retain parsed zone information,
+     * and as a result, {@link java.util.Calendar} will always get context timezone
+     * adjustment regardless of this setting.
      *<p>
-     * As of Jackson 2.8, this feature is supported only by extension modules for Joda
-     * and Java 8 date/tyime datatypes.
+     *<p>
+     * Taking above into account, this feature is supported only by extension modules for
+     * Joda and Java 8 date/tyime datatypes.
      * 
      * @since 2.2
      */
diff --git a/src/main/java/com/fasterxml/jackson/databind/InjectableValues.java b/src/main/java/com/fasterxml/jackson/databind/InjectableValues.java
index a4da378..9f773c3 100644
--- a/src/main/java/com/fasterxml/jackson/databind/InjectableValues.java
+++ b/src/main/java/com/fasterxml/jackson/databind/InjectableValues.java
@@ -2,6 +2,8 @@
 
 import java.util.*;
 
+import com.fasterxml.jackson.databind.util.ClassUtil;
+
 /**
  * Abstract class that defines API for objects that provide value to
  * "inject" during deserialization. An instance of this object
@@ -23,7 +25,7 @@
      *    if available; null if bean has not yet been constructed.
      */
     public abstract Object findInjectableValue(Object valueId, DeserializationContext ctxt,
-            BeanProperty forProperty, Object beanInstance);
+            BeanProperty forProperty, Object beanInstance) throws JsonMappingException;
 
     /*
     /**********************************************************
@@ -63,11 +65,13 @@
         
         @Override
         public Object findInjectableValue(Object valueId, DeserializationContext ctxt,
-                BeanProperty forProperty, Object beanInstance)
+                BeanProperty forProperty, Object beanInstance) throws JsonMappingException
         {
             if (!(valueId instanceof String)) {
-                String type = (valueId == null) ? "[null]" : valueId.getClass().getName();
-                throw new IllegalArgumentException("Unrecognized inject value id type ("+type+"), expecting String");
+                ctxt.reportBadDefinition(ClassUtil.classOf(valueId),
+                        String.format(
+                        "Unrecognized inject value id type (%s), expecting String",
+                        ClassUtil.classNameOf(valueId)));
             }
             String key = (String) valueId;
             Object ob = _values.get(key);
diff --git a/src/main/java/com/fasterxml/jackson/databind/JavaType.java b/src/main/java/com/fasterxml/jackson/databind/JavaType.java
index 292cc39..3fdbb2b 100644
--- a/src/main/java/com/fasterxml/jackson/databind/JavaType.java
+++ b/src/main/java/com/fasterxml/jackson/databind/JavaType.java
@@ -211,15 +211,7 @@
         if (subclass == _class) { // can still optimize for simple case
             return this;
         }
-        JavaType result = _narrow(subclass);
-        // TODO: these checks should NOT actually be needed; above should suffice:
-        if (_valueHandler != result.<Object>getValueHandler()) {
-            result = result.withValueHandler(_valueHandler);
-        }
-        if (_typeHandler != result.<Object>getTypeHandler()) {
-            result = result.withTypeHandler(_typeHandler);
-        }
-        return result;
+        return  _narrow(subclass);
     }
 
     @Deprecated // since 2.7
@@ -257,7 +249,14 @@
      * @since 2.6
      */
     public final boolean isTypeOrSubTypeOf(Class<?> clz) {
-        return (_class == clz) || (clz.isAssignableFrom(_class));
+        return (_class == clz) || clz.isAssignableFrom(_class);
+    }
+
+    /**
+     * @since 2.9
+     */
+    public final boolean isTypeOrSuperTypeOf(Class<?> clz) {
+        return (_class == clz) || _class.isAssignableFrom(clz);
     }
 
     @Override
diff --git a/src/main/java/com/fasterxml/jackson/databind/JsonDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/JsonDeserializer.java
index 1e559a7..469055f 100644
--- a/src/main/java/com/fasterxml/jackson/databind/JsonDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/JsonDeserializer.java
@@ -4,10 +4,11 @@
 import java.util.Collection;
 
 import com.fasterxml.jackson.core.*;
-import com.fasterxml.jackson.databind.deser.BeanDeserializerFactory;
-import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
+
+import com.fasterxml.jackson.databind.deser.*;
 import com.fasterxml.jackson.databind.deser.impl.ObjectIdReader;
 import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.util.AccessPattern;
 import com.fasterxml.jackson.databind.util.NameTransformer;
 
 /**
@@ -29,7 +30,7 @@
  *<p>
  * In addition, to support per-property annotations (to configure aspects
  * of deserialization on per-property basis), deserializers may want
- * to implement 
+ * to implement
  * {@link com.fasterxml.jackson.databind.deser.ContextualDeserializer},
  * which allows specialization of deserializers: call to
  * {@link com.fasterxml.jackson.databind.deser.ContextualDeserializer#createContextual}
@@ -43,6 +44,7 @@
  * contextualization.
  */
 public abstract class JsonDeserializer<T>
+    implements NullValueProvider // since 2.9
 {
     /*
     /**********************************************************
@@ -80,8 +82,8 @@
      *  after the @class. Thus, if you want your method to work correctly
      *  both with and without polymorphism, you must begin your method with:
      *  <pre>
-     *       if (jp.getCurrentToken() == JsonToken.START_OBJECT) {
-     *         jp.nextToken();
+     *       if (p.getCurrentToken() == JsonToken.START_OBJECT) {
+     *         p.nextToken();
      *       }
      *  </pre>
      * This results in the stream pointing to the field name, so that
@@ -121,9 +123,12 @@
      * update-existing-value operation (esp. immutable types)
      */
     public T deserialize(JsonParser p, DeserializationContext ctxt, T intoValue)
-        throws IOException, JsonProcessingException
+        throws IOException
     {
-        throw new UnsupportedOperationException("Can not update object of type "
+        if (ctxt.isEnabled(MapperFeature.IGNORE_MERGE_FOR_UNMERGEABLE)) {
+            return deserialize(p, ctxt);
+        }
+        throw new UnsupportedOperationException("Cannot update object of type "
                 +intoValue.getClass().getName()+" (by deserializer of type "+getClass().getName()+")");
     }
 
@@ -251,10 +256,10 @@
 
     /*
     /**********************************************************
-    /* Other accessors
+    /* Default NullValueProvider implementation
     /**********************************************************
      */
-    
+
     /**
      * Method that can be called to determine value to be used for
      * representing null values (values deserialized when JSON token
@@ -270,12 +275,44 @@
      * 
      * @since 2.6 Added to replace earlier no-arguments variant
      */
+    @Override
     public T getNullValue(DeserializationContext ctxt) throws JsonMappingException {
         // Change the direction in 2.7
         return getNullValue();
     }
 
     /**
+     * Default implementation indicates that "null value" to use for input null
+     * is simply Java `null` for all deserializers, unless overridden by sub-classes.
+     * This information may be used as optimization.
+     */
+    @Override
+    public AccessPattern getNullAccessPattern() {
+        // Default implementation assumes that the null value does not vary, which
+        // is usually the case for most implementations. But it is not necessarily
+        // `null`; so sub-classes may want to refine further.
+        return AccessPattern.CONSTANT;
+    }
+
+    /**
+     * This method may be called in conjunction with calls to
+     * {@link #getEmptyValue(DeserializationContext)}, to check whether it needs
+     * to be called just once (static values), or each time empty value is
+     * needed.
+     *
+     * @since 2.9
+     */
+    public AccessPattern getEmptyAccessPattern() {
+        return AccessPattern.DYNAMIC;
+    }
+
+    /*
+    /**********************************************************
+    /* Other accessors
+    /**********************************************************
+     */
+    
+    /**
      * Method called to determine value to be used for "empty" values
      * (most commonly when deserializing from empty JSON Strings).
      * Usually this is same as {@link #getNullValue} (which in turn
@@ -286,23 +323,24 @@
      * Since version 2.6 (in which the context argument was added), call is
      * expected to be made each and every time an empty value is needed.
      *<p>
-     * Default implementation simple calls {@link #getNullValue} and
+     * Since version 2.9 does not require return of `T` any more.
+     *<p>
+     * Default implementation simply calls {@link #getNullValue} and
      * returns value.
      *
      * @since 2.6 Added to replace earlier no-arguments variant
      */
-    public T getEmptyValue(DeserializationContext ctxt) throws JsonMappingException {
-        // Change the direction in 2.7
-        return getEmptyValue();
+    public Object getEmptyValue(DeserializationContext ctxt) throws JsonMappingException {
+        return getNullValue(ctxt);
     }
-    
+
     /**
      * Accessor that can be used to check whether this deserializer
      * is expecting to possibly get an Object Identifier value instead of full value
      * serialization, and if so, should be able to resolve it to actual
      * Object instance to return as deserialized value.
      *<p>
-     * Default implementation returns null, as support can not be implemented
+     * Default implementation returns null, as support cannot be implemented
      * generically. Some standard deserializers (most notably
      * {@link com.fasterxml.jackson.databind.deser.BeanDeserializer})
      * do implement this feature, and may return reader instance, depending on exact
@@ -324,10 +362,33 @@
      */
     public SettableBeanProperty findBackReference(String refName)
     {
-        throw new IllegalArgumentException("Can not handle managed/back reference '"+refName
+        throw new IllegalArgumentException("Cannot handle managed/back reference '"+refName
                 +"': type: value deserializer of type "+getClass().getName()+" does not support them");
     }
 
+    /**
+     * Introspection method that may be called to see whether deserializer supports
+     * update of an existing value (aka "merging") or not. Return value should either
+     * be {@link Boolean#FALSE} if update is not supported at all (immutable values);
+     * {@link Boolean#TRUE} if update should usually work (regular POJOs, for example),
+     * or <code>null</code> if this is either not known, or may sometimes work.
+     *<p>
+     * Information gathered is typically used to either prevent merging update for
+     * property (either by skipping, if based on global defaults; or by exception during
+     * deserialization construction if explicit attempt made) if {@link Boolean#FALSE}
+     * returned, or inclusion if {@link Boolean#TRUE} is specified. If "unknown" case
+     * (<code>null</code> returned) behavior is to exclude property if global defaults
+     * used; or to allow if explicit per-type or property merging is defined.
+     *<p>
+     * Default implementation returns <code>null</code> to allow explicit per-type
+     * or per-property attempts.
+     *
+     * @since 2.9
+     */
+    public Boolean supportsUpdate(DeserializationConfig config) {
+        return null;
+    }
+
     /*
     /**********************************************************
     /* Deprecated methods
@@ -344,8 +405,8 @@
      * @deprecated Since 2.6 Use overloaded variant that takes context argument
      */
     @Deprecated
-    public T getEmptyValue() { return getNullValue(); }
-    
+    public Object getEmptyValue() { return getNullValue(); }
+
     /*
     /**********************************************************
     /* Helper classes
diff --git a/src/main/java/com/fasterxml/jackson/databind/JsonMappingException.java b/src/main/java/com/fasterxml/jackson/databind/JsonMappingException.java
index 00896ea..39a2980 100644
--- a/src/main/java/com/fasterxml/jackson/databind/JsonMappingException.java
+++ b/src/main/java/com/fasterxml/jackson/databind/JsonMappingException.java
@@ -85,7 +85,7 @@
         public Reference(Object from, String fieldName) {
             _from = from;
             if (fieldName == null) {
-                throw new NullPointerException("Can not pass null fieldName");
+                throw new NullPointerException("Cannot pass null fieldName");
             }
             _fieldName = fieldName;
         }
@@ -326,6 +326,9 @@
      * Factory method used when "upgrading" an {@link IOException} into
      * {@link JsonMappingException}: usually only needed to comply with
      * a signature.
+     *<p>
+     * NOTE: since 2.9 should usually NOT be used on input-side (deserialization)
+     *    exceptions; instead use method(s) of <code>InputMismatchException</code>
      * 
      * @since 2.1
      */
diff --git a/src/main/java/com/fasterxml/jackson/databind/JsonNode.java b/src/main/java/com/fasterxml/jackson/databind/JsonNode.java
index 142ed73..6a57c6c 100644
--- a/src/main/java/com/fasterxml/jackson/databind/JsonNode.java
+++ b/src/main/java/com/fasterxml/jackson/databind/JsonNode.java
@@ -99,18 +99,18 @@
     }
 
     @Override
-    public final boolean isMissingNode() {
-        return getNodeType() == JsonNodeType.MISSING;
+    public boolean isMissingNode() {
+        return false;
     }
 
     @Override
-    public final boolean isArray() {
-        return getNodeType() == JsonNodeType.ARRAY;
+    public boolean isArray() {
+        return false;
     }
 
     @Override
-    public final boolean isObject() {
-        return getNodeType() == JsonNodeType.OBJECT;
+    public boolean isObject() {
+        return false;
     }
 
     /**
@@ -559,7 +559,7 @@
      * and 1 (true), and Strings are parsed using default Java language integer
      * parsing rules.
      *<p>
-     * If representation can not be converted to an int (including structured types
+     * If representation cannot be converted to an int (including structured types
      * like Objects and Arrays),
      * default value of <b>0</b> will be returned; no exceptions are thrown.
      */
@@ -573,7 +573,7 @@
      * and 1 (true), and Strings are parsed using default Java language integer
      * parsing rules.
      *<p>
-     * If representation can not be converted to an int (including structured types
+     * If representation cannot be converted to an int (including structured types
      * like Objects and Arrays),
      * specified <b>defaultValue</b> will be returned; no exceptions are thrown.
      */
@@ -587,7 +587,7 @@
      * and 1 (true), and Strings are parsed using default Java language integer
      * parsing rules.
      *<p>
-     * If representation can not be converted to an long (including structured types
+     * If representation cannot be converted to an long (including structured types
      * like Objects and Arrays),
      * default value of <b>0</b> will be returned; no exceptions are thrown.
      */
@@ -601,7 +601,7 @@
      * and 1 (true), and Strings are parsed using default Java language integer
      * parsing rules.
      *<p>
-     * If representation can not be converted to an long (including structured types
+     * If representation cannot be converted to an long (including structured types
      * like Objects and Arrays),
      * specified <b>defaultValue</b> will be returned; no exceptions are thrown.
      */
@@ -615,7 +615,7 @@
      * and 1.0 (true), and Strings are parsed using default Java language integer
      * parsing rules.
      *<p>
-     * If representation can not be converted to an int (including structured types
+     * If representation cannot be converted to an int (including structured types
      * like Objects and Arrays),
      * default value of <b>0.0</b> will be returned; no exceptions are thrown.
      */
@@ -629,7 +629,7 @@
      * and 1.0 (true), and Strings are parsed using default Java language integer
      * parsing rules.
      *<p>
-     * If representation can not be converted to an int (including structured types
+     * If representation cannot be converted to an int (including structured types
      * like Objects and Arrays),
      * specified <b>defaultValue</b> will be returned; no exceptions are thrown.
      */
@@ -643,7 +643,7 @@
      * 0 maps to false
      * and Strings 'true' and 'false' map to corresponding values.
      *<p>
-     * If representation can not be converted to a boolean value (including structured types
+     * If representation cannot be converted to a boolean value (including structured types
      * like Objects and Arrays),
      * default value of <b>false</b> will be returned; no exceptions are thrown.
      */
@@ -657,7 +657,7 @@
      * 0 maps to false
      * and Strings 'true' and 'false' map to corresponding values.
      *<p>
-     * If representation can not be converted to a boolean value (including structured types
+     * If representation cannot be converted to a boolean value (including structured types
      * like Objects and Arrays),
      * specified <b>defaultValue</b> will be returned; no exceptions are thrown.
      */
@@ -896,7 +896,7 @@
      */
     public JsonNode with(String propertyName) {
         throw new UnsupportedOperationException("JsonNode not of type ObjectNode (but "
-                +getClass().getName()+"), can not call with() on it");
+                +getClass().getName()+"), cannot call with() on it");
     }
 
     /**
@@ -909,7 +909,7 @@
      */
     public JsonNode withArray(String propertyName) {
         throw new UnsupportedOperationException("JsonNode not of type ObjectNode (but "
-                +getClass().getName()+"), can not call withArray() on it");
+                +getClass().getName()+"), cannot call withArray() on it");
     }
 
     /*
diff --git a/src/main/java/com/fasterxml/jackson/databind/JsonSerializable.java b/src/main/java/com/fasterxml/jackson/databind/JsonSerializable.java
index bd6f77b..a08898a 100644
--- a/src/main/java/com/fasterxml/jackson/databind/JsonSerializable.java
+++ b/src/main/java/com/fasterxml/jackson/databind/JsonSerializable.java
@@ -31,12 +31,10 @@
      * Serialization method called when additional type information is
      * expected to be included in serialization, for deserialization to use.
      *<p>
-     * Usually implementation consists of a call to one of methods
-     * in {@link TypeSerializer} (such as {@link TypeSerializer#writeTypePrefixForObject(Object, JsonGenerator)})
+     * Usually implementation consists of a call to {@link TypeSerializer#writeTypePrefix}
      * followed by serialization of contents,
-     * followed by another call to {@link TypeSerializer}
-     * (such as {@link TypeSerializer#writeTypeSuffixForObject(Object, JsonGenerator)}).
-     * Exact methods to call in {@link TypeSerializer} depend on shape of JSON Object used
+     * followed by a call to {@link TypeSerializer#writeTypeSuffix}).
+     * Details of the type id argument to pass depend on shape of JSON Object used
      * (Array, Object or scalar like String/Number/Boolean).
      *<p>
      * Note that some types (most notably, "natural" types: String, Integer,
diff --git a/src/main/java/com/fasterxml/jackson/databind/JsonSerializer.java b/src/main/java/com/fasterxml/jackson/databind/JsonSerializer.java
index 3506cd2..11c01b2 100644
--- a/src/main/java/com/fasterxml/jackson/databind/JsonSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/JsonSerializer.java
@@ -24,7 +24,7 @@
  * with null values -- caller <b>must</b> handle null values, usually
  * by calling {@link SerializerProvider#findNullValueSerializer} to obtain
  * serializer to use.
- * This also means that custom serializers can not be directly used to change
+ * This also means that custom serializers cannot be directly used to change
  * the output to produce when serializing null values.
  *<p>
  * If serializer is an aggregate one -- meaning it delegates handling of some
@@ -120,7 +120,7 @@
      *   serializing Objects value contains, if any.
      */
     public abstract void serialize(T value, JsonGenerator gen, SerializerProvider serializers)
-        throws IOException, JsonProcessingException;
+        throws IOException;
 
     /**
      * Method that can be called to ask implementation to serialize
@@ -157,8 +157,9 @@
         if (clz == null) {
             clz = value.getClass();
         }
-        serializers.reportMappingProblem("Type id handling not implemented for type %s (by serializer of type %s)",
-                clz.getName(), getClass().getName());
+        serializers.reportBadDefinition(clz, String.format(
+                "Type id handling not implemented for type %s (by serializer of type %s)",
+                clz.getName(), getClass().getName()));
     }
 
     /*
@@ -188,7 +189,7 @@
      * Default implementation will consider only null values to be empty.
      * 
      * @deprecated Since 2.5 Use {@link #isEmpty(SerializerProvider, Object)} instead;
-     *   will be removed from 2.9
+     *   will be removed from 3.0
      */
     @Deprecated
     public boolean isEmpty(T value) {
@@ -272,7 +273,7 @@
     public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType type)
         throws JsonMappingException
     {
-        if (visitor != null) visitor.expectAnyFormat(type);
+        visitor.expectAnyFormat(type);
     }
 
     /*
diff --git a/src/main/java/com/fasterxml/jackson/databind/KeyDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/KeyDeserializer.java
index 90dd56d..217527d 100644
--- a/src/main/java/com/fasterxml/jackson/databind/KeyDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/KeyDeserializer.java
@@ -2,8 +2,6 @@
 
 import java.io.IOException;
 
-import com.fasterxml.jackson.core.*;
-
 /**
  * Abstract class that defines API used for deserializing JSON content
  * field names into Java Map keys. These deserializers are only used
@@ -15,7 +13,7 @@
      * Method called to deserialize a {@link java.util.Map} key from JSON property name.
      */
     public abstract Object deserializeKey(String key, DeserializationContext ctxt)
-        throws IOException, JsonProcessingException;
+        throws IOException;
 
     /**
      * This marker class is only to be used with annotations, to
diff --git a/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java b/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java
index e163b5f..81884ad 100644
--- a/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java
+++ b/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java
@@ -18,7 +18,7 @@
 {
     /*
     /******************************************************
-    /* Introspection features
+    /* General introspection features
     /******************************************************
      */
 
@@ -33,6 +33,43 @@
     USE_ANNOTATIONS(true),
 
     /**
+     * Feature that determines whether otherwise regular "getter"
+     * methods (but only ones that handle Collections and Maps,
+     * not getters of other type)
+     * can be used for purpose of getting a reference to a Collection
+     * and Map to modify the property, without requiring a setter
+     * method.
+     * This is similar to how JAXB framework sets Collections and
+     * Maps: no setter is involved, just setter.
+     *<p>
+     * Note that such getters-as-setters methods have lower
+     * precedence than setters, so they are only used if no
+     * setter is found for the Map/Collection property.
+     *<p>
+     * Feature is enabled by default.
+     */
+    USE_GETTERS_AS_SETTERS(true),
+
+    /**
+     * Feature that determines how <code>transient</code> modifier for fields
+     * is handled: if disabled, it is only taken to mean exclusion of the field
+     * as accessor; if true, it is taken to imply removal of the whole property.
+     *<p>
+     * Feature is disabled by default, meaning that existence of `transient`
+     * for a field does not necessarily lead to ignoral of getters or setters
+     * but just ignoring the use of field for access.
+     *
+     * @since 2.6
+     */
+    PROPAGATE_TRANSIENT_MARKER(false),
+
+    /*
+    /******************************************************
+    /* Introspection-based property auto-detection
+    /******************************************************
+     */
+
+    /**
      * Feature that determines whether "creator" methods are
      * automatically detected by consider public constructors,
      * and static single argument methods with name "valueOf".
@@ -61,7 +98,7 @@
      *<p>
      * Feature is enabled by default.
      */
-     AUTO_DETECT_FIELDS(true),
+    AUTO_DETECT_FIELDS(true),
     
     /**
      * Feature that determines whether regular "getter" methods are
@@ -98,22 +135,22 @@
      */
     AUTO_DETECT_IS_GETTERS(true),
 
-     /**
-      * Feature that determines whether "setter" methods are
-      * automatically detected based on standard Bean naming convention
-      * or not. If yes, then all public one-argument methods that
-      * start with prefix "set"
-      * are considered setters. If disabled, only methods explicitly
-      * annotated are considered setters.
-      *<p>
-      * Note that this feature has lower precedence than per-class
-      * annotations, and is only used if there isn't more granular
-      * configuration available.
-      *<P>
-      * Feature is enabled by default.
-      */
-     AUTO_DETECT_SETTERS(true),
-     
+    /**
+     * Feature that determines whether "setter" methods are
+     * automatically detected based on standard Bean naming convention
+     * or not. If yes, then all public one-argument methods that
+     * start with prefix "set"
+     * are considered setters. If disabled, only methods explicitly
+     * annotated are considered setters.
+     *<p>
+     * Note that this feature has lower precedence than per-class
+     * annotations, and is only used if there isn't more granular
+     * configuration available.
+     *<P>
+     * Feature is enabled by default.
+     */
+    AUTO_DETECT_SETTERS(true),
+
     /**
      * Feature that determines whether getters (getter methods)
      * can be auto-detected if there is no matching mutator (setter,
@@ -126,22 +163,61 @@
     REQUIRE_SETTERS_FOR_GETTERS(false),
 
     /**
-     * Feature that determines whether otherwise regular "getter"
-     * methods (but only ones that handle Collections and Maps,
-     * not getters of other type)
-     * can be used for purpose of getting a reference to a Collection
-     * and Map to modify the property, without requiring a setter
-     * method.
-     * This is similar to how JAXB framework sets Collections and
-     * Maps: no setter is involved, just setter.
+     * Feature that determines whether member fields declared as 'final' may
+     * be auto-detected to be used mutators (used to change value of the logical
+     * property) or not. If enabled, 'final' access modifier has no effect, and
+     * such fields may be detected according to usual visibility and inference
+     * rules; if disabled, such fields are NOT used as mutators except if
+     * explicitly annotated for such use.
      *<p>
-     * Note that such getters-as-setters methods have lower
-     * precedence than setters, so they are only used if no
-     * setter is found for the Map/Collection property.
+     * Feature is enabled by default, for backwards compatibility reasons.
+     *
+     * @since 2.2
+     */
+    ALLOW_FINAL_FIELDS_AS_MUTATORS(true),
+
+    /**
+     * Feature that determines whether member mutators (fields and
+     * setters) may be "pulled in" even if they are not visible,
+     * as long as there is a visible accessor (getter or field) with same name.
+     * For example: field "value" may be inferred as mutator,
+     * if there is visible or explicitly marked getter "getValue()".
+     * If enabled, inferring is enabled; otherwise (disabled) only visible and
+     * explicitly annotated accessors are ever used.
+     *<p>
+     * Note that 'getters' are never inferred and need to be either visible (including
+     * bean-style naming) or explicitly annotated.
      *<p>
      * Feature is enabled by default.
+     *
+     * @since 2.2
      */
-    USE_GETTERS_AS_SETTERS(true),
+    INFER_PROPERTY_MUTATORS(true),
+
+    /**
+     * Feature that determines handling of <code>java.beans.ConstructorProperties<code>
+     * annotation: when enabled, it is considered as alias of
+     * {@link com.fasterxml.jackson.annotation.JsonCreator}, to mean that constructor
+     * should be considered a property-based Creator; when disabled, only constructor
+     * parameter name information is used, but constructor is NOT considered an explicit
+     * Creator (although may be discovered as one using other annotations or heuristics).
+     *<p>
+     * Feature is mostly used to help interoperability with frameworks like Lombok
+     * that may automatically generate <code>ConstructorProperties</code> annotation
+     * but without necessarily meaning that constructor should be used as Creator
+     * for deserialization.
+     *<p>
+     * Feature is enabled by default.
+     *
+     * @since 2.9
+     */
+    INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES(true),
+
+    /*
+    /******************************************************
+    /* Access modifier handling
+    /******************************************************
+     */
 
     /**
      * Feature that determines whether method and field access
@@ -187,51 +263,6 @@
      */
     OVERRIDE_PUBLIC_ACCESS_MODIFIERS(true),
 
-    /**
-     * Feature that determines whether member mutators (fields and
-     * setters) may be "pulled in" even if they are not visible,
-     * as long as there is a visible accessor (getter or field) with same name.
-     * For example: field "value" may be inferred as mutator,
-     * if there is visible or explicitly marked getter "getValue()".
-     * If enabled, inferring is enabled; otherwise (disabled) only visible and
-     * explicitly annotated accessors are ever used.
-     *<p>
-     * Note that 'getters' are never inferred and need to be either visible (including
-     * bean-style naming) or explicitly annotated.
-     *<p>
-     * Feature is enabled by default.
-     * 
-     * @since 2.2
-     */
-    INFER_PROPERTY_MUTATORS(true),
-
-    /**
-     * Feature that determines whether member fields declared as 'final' may
-     * be auto-detected to be used mutators (used to change value of the logical
-     * property) or not. If enabled, 'final' access modifier has no effect, and
-     * such fields may be detected according to usual visibility and inference
-     * rules; if disabled, such fields are NOT used as mutators except if
-     * explicitly annotated for such use.
-     *<p>
-     * Feature is enabled by default, for backwards compatibility reasons.
-     * 
-     * @since 2.2
-     */
-    ALLOW_FINAL_FIELDS_AS_MUTATORS(true),
-
-    /**
-     * Feature that determines how <code>transient</code> modifier for fields
-     * is handled: if disabled, it is only taken to mean exclusion of the field
-     * as accessor; if true, removal of the whole property.
-     *<p>
-     * Feature is disabled by default, meaning that existence of `transient`
-     * for a field does not necessarily lead to ignoral of getters or setters
-     * but just ignoring the use of field for access.
-     *
-     * @since 2.6
-     */
-    PROPAGATE_TRANSIENT_MARKER(false),
-
     /*
     /******************************************************
     /* Type-handling features
@@ -255,6 +286,20 @@
      */
     USE_STATIC_TYPING(false),
 
+    /**
+     * Feature that specifies whether the declared base type of a polymorphic value
+     * is to be used as the "default" implementation, if no explicit default class
+     * is specified via {@code @JsonTypeInfo.defaultImpl} annotation.
+     *<p>
+     * Note that feature only has effect on deserialization of regular polymorphic properties:
+     * it does NOT affect non-polymorphic cases, and is unlikely to work with Default Typing.
+     *<p>
+     * Feature is disabled by default for backwards compatibility.
+     *
+     * @since 2.9.6
+     */
+    USE_BASE_TYPE_AS_DEFAULT_IMPL(false),
+
     /*
     /******************************************************
     /* View-related features
@@ -307,6 +352,7 @@
     /* Name-related features
     /******************************************************
      */
+
     /**
      * Feature that will allow for more forgiving deserialization of incoming JSON.
      * If enabled, the bean properties will be matched using their lower-case equivalents,
@@ -322,7 +368,20 @@
      * @since 2.5
      */
     ACCEPT_CASE_INSENSITIVE_PROPERTIES(false),
-    
+
+
+    /**
+     * Feature that determines if Enum deserialization should be case sensitive or not.
+     * If enabled, Enum deserialization will ignore case, that is, case of incoming String
+     * value and enum id (dependant on other settings, either `name()`, `toString()`, or
+     * explicit override) do not need to match.
+     * <p>
+     * Feature is disabled by default.
+     *
+     * @since 2.9
+     */
+    ACCEPT_CASE_INSENSITIVE_ENUMS(false),
+
     /**
      * Feature that can be enabled to make property names be
      * overridden by wrapper name (usually detected with annotations
@@ -366,6 +425,34 @@
 
     /*
     /******************************************************
+    /* Coercion features
+    /******************************************************
+     */
+
+    /**
+     * Feature that determines whether coercions from secondary representations are allowed
+     * for simple non-textual scalar types: numbers and booleans. This includes `primitive`
+     * types and their wrappers, but excludes `java.lang.String` and date/time types.
+     *<p>
+     * When feature is disabled, only strictly compatible input may be bound: numbers for
+     * numbers, boolean values for booleans. When feature is enabled, conversions from
+     * JSON String are allowed, as long as textual value matches (for example, String
+     * "true" is allowed as equivalent of JSON boolean token `true`; or String "1.0"
+     * for `double`).
+     *<p>
+     * Note that it is possible that other configurability options can override this
+     * in closer scope (like on per-type or per-property basis); this is just the global
+     * default.
+     *<p>
+     * Feature is enabled by default (for backwards compatibility since this was the
+     * default behavior)
+     *
+     * @since 2.9
+     */
+    ALLOW_COERCION_OF_SCALARS(true),
+
+    /*
+    /******************************************************
     /* Other features
     /******************************************************
      */
@@ -387,8 +474,22 @@
      *
      * @since 2.5
      */
-    IGNORE_DUPLICATE_MODULE_REGISTRATIONS(true)
-    
+    IGNORE_DUPLICATE_MODULE_REGISTRATIONS(true),
+
+    /**
+     * Setting that determines what happens if an attempt is made to explicitly
+     * "merge" value of a property, where value does not support merging; either
+     * merging is skipped and new value is created (<code>true</code>) or
+     * an exception is thrown (false).
+     *<p>
+     * Feature is disabled by default since non-mergeable property types are ignored
+     * even if defaults call for merging, and usually explicit per-type or per-property
+     * settings for such types should result in an exception.
+     *
+     * @since 2.9
+     */
+    IGNORE_MERGE_FOR_UNMERGEABLE(true)
+
     ;
 
     private final boolean _defaultState;
diff --git a/src/main/java/com/fasterxml/jackson/databind/Module.java b/src/main/java/com/fasterxml/jackson/databind/Module.java
index d7d0d01..1fe6096 100644
--- a/src/main/java/com/fasterxml/jackson/databind/Module.java
+++ b/src/main/java/com/fasterxml/jackson/databind/Module.java
@@ -1,5 +1,7 @@
 package com.fasterxml.jackson.databind;
 
+import java.util.Collection;
+
 import com.fasterxml.jackson.core.*;
 import com.fasterxml.jackson.databind.cfg.MutableConfigOverride;
 import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
@@ -297,6 +299,14 @@
          * they have), using specified type names.
          */
         public void registerSubtypes(NamedType... subtypes);
+
+        /**
+         * Method for registering specified classes as subtypes (of supertype(s)
+         * they have)
+         *
+         * @since 2.9
+         */
+        public void registerSubtypes(Collection<Class<?>> subtypes);
         
         /**
          * Method used for defining mix-in annotations to use for augmenting
diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java
index cb64cee..adfb09b 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java
@@ -17,13 +17,9 @@
 import com.fasterxml.jackson.core.type.ResolvedType;
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.core.util.*;
-import com.fasterxml.jackson.databind.cfg.BaseSettings;
-import com.fasterxml.jackson.databind.cfg.ContextAttributes;
-import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
-import com.fasterxml.jackson.databind.cfg.MapperConfig;
-import com.fasterxml.jackson.databind.cfg.MutableConfigOverride;
-import com.fasterxml.jackson.databind.cfg.ConfigOverrides;
+import com.fasterxml.jackson.databind.cfg.*;
 import com.fasterxml.jackson.databind.deser.*;
+import com.fasterxml.jackson.databind.exc.MismatchedInputException;
 import com.fasterxml.jackson.databind.introspect.*;
 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
 import com.fasterxml.jackson.databind.jsontype.*;
@@ -80,7 +76,7 @@
  * on Streaming API).
  *<p> 
  * Mapper instances are fully thread-safe provided that ALL configuration of the
- * instance occurs before ANY read or write calls. If configuration of a mapper
+ * instance occurs before ANY read or write calls. If configuration of a mapper instance
  * is modified after first usage, changes may or may not take effect, and configuration
  * calls themselves may fail.
  * If you need to use different configuration, you have two main possibilities:
@@ -94,7 +90,7 @@
  *  </li>
  * <li>If the specific kind of configurability is not available via {@link ObjectReader} and
  *   {@link ObjectWriter}, you may need to use multiple {@link ObjectMapper} instead (for example:
- *   you can not change mix-in annotations on-the-fly; or, set of custom (de)serializers).
+ *   you cannot change mix-in annotations on-the-fly; or, set of custom (de)serializers).
  *   To help with this usage, you may want to use method {@link #copy()} which creates a clone
  *   of the mapper with specific configuration, and allows configuration of the copied instance
  *   before it gets used. Note that {@link #copy} operation is as expensive as constructing
@@ -112,13 +108,19 @@
  * produce differing deserializers), and that the performance impact
  * greatest at root level (since it'll essentially cache the full
  * graph of deserializers involved).
+ *<p>
+ * Notes on security: use "default typing" feature (see {@link #enableDefaultTyping()})
+ * is a potential security risk, if used with untrusted content (content generated by
+ * untrusted external parties). If so, you may want to construct a custom 
+ * {@link TypeResolverBuilder} implementation to limit possible types to instantiate,
+ * (using {@link #setDefaultTyping}).
  */
 public class ObjectMapper
     extends ObjectCodec
     implements Versioned,
         java.io.Serializable // as of 2.1
 {
-    private static final long serialVersionUID = 1L;
+    private static final long serialVersionUID = 2L; // as of 2.9
 
     /*
     /**********************************************************
@@ -134,11 +136,15 @@
      *<p>
      * Since 2.4 there are special exceptions for JSON Tree model
      * types (sub-types of {@link TreeNode}: default typing is never
-     * applied to them
-     * (see <a href="https://github.com/FasterXML/jackson-databind/issues/88">databind#88</a> for details)
-     *<p>
+     * applied to them.
      * Since 2.8(.4) additional checks are made to avoid attempts at default
      * typing primitive-valued properties.
+     *<p>
+     * NOTE: use of Default Typing can be a potential security risk if incoming
+     * content comes from untrusted sources, and it is recommended that this
+     * is either not done, or, if enabled, use {@link #setDefaultTyping}
+     * passing a custom {@link TypeResolverBuilder} implementation that white-lists
+     * legal types to use.
      */
     public enum DefaultTyping {
         /**
@@ -284,16 +290,14 @@
     // 16-May-2009, tatu: Ditto ^^^
     protected final static AnnotationIntrospector DEFAULT_ANNOTATION_INTROSPECTOR = new JacksonAnnotationIntrospector();
 
-    protected final static VisibilityChecker<?> STD_VISIBILITY_CHECKER = VisibilityChecker.Std.defaultInstance();
-
     /**
      * Base settings contain defaults used for all {@link ObjectMapper}
      * instances.
      */
     protected final static BaseSettings DEFAULT_BASE = new BaseSettings(
-            null, // can not share global ClassIntrospector any more (2.5+)
+            null, // cannot share global ClassIntrospector any more (2.5+)
             DEFAULT_ANNOTATION_INTROSPECTOR,
-            STD_VISIBILITY_CHECKER, null, TypeFactory.defaultInstance(),
+             null, TypeFactory.defaultInstance(),
             null, StdDateFormat.instance, null,
             Locale.getDefault(),
             null, // to indicate "use Jackson default TimeZone" (UTC since Jackson 2.7)
@@ -334,9 +338,9 @@
      * Currently active per-type configuration overrides, accessed by
      * declared type of property.
      *
-     * @since 2.8
+     * @since 2.9
      */
-    protected ConfigOverrides _propertyOverrides;
+    protected final ConfigOverrides _configOverrides;
 
     /*
     /**********************************************************
@@ -497,12 +501,14 @@
         _subtypeResolver = src._subtypeResolver;
         _typeFactory = src._typeFactory;
         _injectableValues = src._injectableValues;
-        _propertyOverrides = src._propertyOverrides.copy();
+        _configOverrides = src._configOverrides.copy();
         _mixIns = src._mixIns.copy();
 
         RootNameLookup rootNames = new RootNameLookup();
-        _serializationConfig = new SerializationConfig(src._serializationConfig, _mixIns, rootNames, _propertyOverrides);
-        _deserializationConfig = new DeserializationConfig(src._deserializationConfig, _mixIns, rootNames, _propertyOverrides);
+        _serializationConfig = new SerializationConfig(src._serializationConfig,
+                _mixIns, rootNames, _configOverrides);
+        _deserializationConfig = new DeserializationConfig(src._deserializationConfig,
+                _mixIns, rootNames,  _configOverrides);
         _serializerProvider = src._serializerProvider.copy();
         _deserializationContext = src._deserializationContext.copy();
 
@@ -555,12 +561,11 @@
         SimpleMixInResolver mixins = new SimpleMixInResolver(null);
         _mixIns = mixins;
         BaseSettings base = DEFAULT_BASE.withClassIntrospector(defaultClassIntrospector());
-        ConfigOverrides propOverrides = new ConfigOverrides();
-        _propertyOverrides = propOverrides;
+        _configOverrides = new ConfigOverrides();
         _serializationConfig = new SerializationConfig(base,
-                    _subtypeResolver, mixins, rootNames, propOverrides);
+                    _subtypeResolver, mixins, rootNames, _configOverrides);
         _deserializationConfig = new DeserializationConfig(base,
-                    _subtypeResolver, mixins, rootNames, propOverrides);
+                    _subtypeResolver, mixins, rootNames, _configOverrides);
 
         // Some overrides we may need
         final boolean needOrder = _jsonFactory.requiresPropertyOrdering();
@@ -618,6 +623,7 @@
     protected void _checkInvalidCopy(Class<?> exp)
     {
         if (getClass() != exp) {
+            // 10-Nov-2016, tatu: could almost use `ClassUtil.verifyMustOverride()` but not quite
             throw new IllegalStateException("Failed copy(): "+getClass().getName()
                     +" (version: "+version()+") does not override copy(); it has to");
         }
@@ -741,8 +747,6 @@
             throw new IllegalArgumentException("Module without defined version");
         }
 
-        final ObjectMapper mapper = this;
-        
         // And then call registration
         module.setupModule(new Module.SetupContext()
         {
@@ -757,7 +761,7 @@
             @Override
             public <C extends ObjectCodec> C getOwner() {
                 // why do we need the cast here?!?
-                return (C) mapper;
+                return (C) ObjectMapper.this;
             }
 
             @Override
@@ -767,140 +771,145 @@
             
             @Override
             public boolean isEnabled(MapperFeature f) {
-                return mapper.isEnabled(f);
+                return ObjectMapper.this.isEnabled(f);
             }
 
             @Override
             public boolean isEnabled(DeserializationFeature f) {
-                return mapper.isEnabled(f);
+                return ObjectMapper.this.isEnabled(f);
             }
             
             @Override
             public boolean isEnabled(SerializationFeature f) {
-                return mapper.isEnabled(f);
+                return ObjectMapper.this.isEnabled(f);
             }
 
             @Override
             public boolean isEnabled(JsonFactory.Feature f) {
-                return mapper.isEnabled(f);
+                return ObjectMapper.this.isEnabled(f);
             }
 
             @Override
             public boolean isEnabled(JsonParser.Feature f) {
-                return mapper.isEnabled(f);
+                return ObjectMapper.this.isEnabled(f);
             }
 
             @Override
             public boolean isEnabled(JsonGenerator.Feature f) {
-                return mapper.isEnabled(f);
+                return ObjectMapper.this.isEnabled(f);
             }
 
             // // // Mutant accessors
 
             @Override
             public MutableConfigOverride configOverride(Class<?> type) {
-                return mapper.configOverride(type);
+                return ObjectMapper.this.configOverride(type);
             }
 
             // // // Methods for registering handlers: deserializers
 
             @Override
             public void addDeserializers(Deserializers d) {
-                DeserializerFactory df = mapper._deserializationContext._factory.withAdditionalDeserializers(d);
-                mapper._deserializationContext = mapper._deserializationContext.with(df);
+                DeserializerFactory df = _deserializationContext._factory.withAdditionalDeserializers(d);
+                _deserializationContext = _deserializationContext.with(df);
             }
 
             @Override
             public void addKeyDeserializers(KeyDeserializers d) {
-                DeserializerFactory df = mapper._deserializationContext._factory.withAdditionalKeyDeserializers(d);
-                mapper._deserializationContext = mapper._deserializationContext.with(df);
+                DeserializerFactory df = _deserializationContext._factory.withAdditionalKeyDeserializers(d);
+                _deserializationContext = _deserializationContext.with(df);
             }
 
             @Override
             public void addBeanDeserializerModifier(BeanDeserializerModifier modifier) {
-                DeserializerFactory df = mapper._deserializationContext._factory.withDeserializerModifier(modifier);
-                mapper._deserializationContext = mapper._deserializationContext.with(df);
+                DeserializerFactory df = _deserializationContext._factory.withDeserializerModifier(modifier);
+                _deserializationContext = _deserializationContext.with(df);
             }
             
             // // // Methods for registering handlers: serializers
             
             @Override
             public void addSerializers(Serializers s) {
-                mapper._serializerFactory = mapper._serializerFactory.withAdditionalSerializers(s);
+                _serializerFactory = _serializerFactory.withAdditionalSerializers(s);
             }
 
             @Override
             public void addKeySerializers(Serializers s) {
-                mapper._serializerFactory = mapper._serializerFactory.withAdditionalKeySerializers(s);
+                _serializerFactory = _serializerFactory.withAdditionalKeySerializers(s);
             }
             
             @Override
             public void addBeanSerializerModifier(BeanSerializerModifier modifier) {
-                mapper._serializerFactory = mapper._serializerFactory.withSerializerModifier(modifier);
+                _serializerFactory = _serializerFactory.withSerializerModifier(modifier);
             }
 
             // // // Methods for registering handlers: other
             
             @Override
             public void addAbstractTypeResolver(AbstractTypeResolver resolver) {
-                DeserializerFactory df = mapper._deserializationContext._factory.withAbstractTypeResolver(resolver);
-                mapper._deserializationContext = mapper._deserializationContext.with(df);
+                DeserializerFactory df = _deserializationContext._factory.withAbstractTypeResolver(resolver);
+                _deserializationContext = _deserializationContext.with(df);
             }
 
             @Override
             public void addTypeModifier(TypeModifier modifier) {
-                TypeFactory f = mapper._typeFactory;
+                TypeFactory f = _typeFactory;
                 f = f.withModifier(modifier);
-                mapper.setTypeFactory(f);
+                setTypeFactory(f);
             }
 
             @Override
             public void addValueInstantiators(ValueInstantiators instantiators) {
-                DeserializerFactory df = mapper._deserializationContext._factory.withValueInstantiators(instantiators);
-                mapper._deserializationContext = mapper._deserializationContext.with(df);
+                DeserializerFactory df = _deserializationContext._factory.withValueInstantiators(instantiators);
+                _deserializationContext = _deserializationContext.with(df);
             }
 
             @Override
             public void setClassIntrospector(ClassIntrospector ci) {
-                mapper._deserializationConfig = mapper._deserializationConfig.with(ci);
-                mapper._serializationConfig = mapper._serializationConfig.with(ci);
+                _deserializationConfig = _deserializationConfig.with(ci);
+                _serializationConfig = _serializationConfig.with(ci);
             }
 
             @Override
             public void insertAnnotationIntrospector(AnnotationIntrospector ai) {
-                mapper._deserializationConfig = mapper._deserializationConfig.withInsertedAnnotationIntrospector(ai);
-                mapper._serializationConfig = mapper._serializationConfig.withInsertedAnnotationIntrospector(ai);
+                _deserializationConfig = _deserializationConfig.withInsertedAnnotationIntrospector(ai);
+                _serializationConfig = _serializationConfig.withInsertedAnnotationIntrospector(ai);
             }
             
             @Override
             public void appendAnnotationIntrospector(AnnotationIntrospector ai) {
-                mapper._deserializationConfig = mapper._deserializationConfig.withAppendedAnnotationIntrospector(ai);
-                mapper._serializationConfig = mapper._serializationConfig.withAppendedAnnotationIntrospector(ai);
+                _deserializationConfig = _deserializationConfig.withAppendedAnnotationIntrospector(ai);
+                _serializationConfig = _serializationConfig.withAppendedAnnotationIntrospector(ai);
             }
 
             @Override
             public void registerSubtypes(Class<?>... subtypes) {
-                mapper.registerSubtypes(subtypes);
+                ObjectMapper.this.registerSubtypes(subtypes);
             }
 
             @Override
             public void registerSubtypes(NamedType... subtypes) {
-                mapper.registerSubtypes(subtypes);
+                ObjectMapper.this.registerSubtypes(subtypes);
             }
-            
+
+            @Override
+            public void registerSubtypes(Collection<Class<?>> subtypes) {
+                ObjectMapper.this.registerSubtypes(subtypes);
+            }
+
             @Override
             public void setMixInAnnotations(Class<?> target, Class<?> mixinSource) {
-                mapper.addMixIn(target, mixinSource);
+                addMixIn(target, mixinSource);
             }
             
             @Override
             public void addDeserializationProblemHandler(DeserializationProblemHandler handler) {
-                mapper.addHandler(handler);
+                addHandler(handler);
             }
 
             @Override
             public void setNamingStrategy(PropertyNamingStrategy naming) {
-                mapper.setPropertyNamingStrategy(naming);
+                setPropertyNamingStrategy(naming);
             }
         });
         return this;
@@ -936,14 +945,26 @@
      * 
      * @since 2.2
      */
-    public ObjectMapper registerModules(Iterable<Module> modules)
+    public ObjectMapper registerModules(Iterable<? extends Module> modules)
     {
         for (Module module : modules) {
             registerModule(module);
         }
         return this;
     }
-    
+
+    /**
+     * The set of {@link Module} typeIds that are registered in this
+     * ObjectMapper. By default the typeId for a module is it's full
+     * class name (see {@link Module#getTypeId()}).
+     *
+     * @since 2.9.6
+     */
+    public Set<Object> getRegisteredModuleIds()
+    {
+        return Collections.unmodifiableSet(_registeredModuleTypes);
+    }
+
     /**
      * Method for locating available methods, using JDK {@link ServiceLoader}
      * facility, along with module-provided SPI.
@@ -1088,7 +1109,7 @@
     /**
      * Accessor for the "blueprint" (or, factory) instance, from which instances
      * are created by calling {@link DefaultSerializerProvider#createInstance}.
-     * Note that returned instance can not be directly used as it is not properly
+     * Note that returned instance cannot be directly used as it is not properly
      * configured: to get a properly configured instance to call, use
      * {@link #getSerializerProviderInstance()} instead.
      */
@@ -1199,7 +1220,7 @@
     public final void addMixInAnnotations(Class<?> target, Class<?> mixinSource) {
         addMixIn(target, mixinSource);
     }
-    
+
     /*
     /**********************************************************
     /* Configuration, introspection
@@ -1216,28 +1237,20 @@
     }
 
     /**
-     * @deprecated Since 2.6 use {@link #setVisibility(VisibilityChecker)} instead.
-     */
-    @Deprecated
-    public void setVisibilityChecker(VisibilityChecker<?> vc) {
-        setVisibility(vc);
-    }
-
-    /**
-     * Method for setting currently configured {@link VisibilityChecker},
+     * Method for setting currently configured default {@link VisibilityChecker},
      * object used for determining whether given property element
      * (method, field, constructor) can be auto-detected or not.
-     * This default checker is used if no per-class overrides
-     * are defined.
+     * This default checker is used as the base visibility:
+     * per-class overrides (both via annotations and per-type config overrides)
+     * can further change these settings.
      * 
      * @since 2.6
      */
     public ObjectMapper setVisibility(VisibilityChecker<?> vc) {
-        _deserializationConfig = _deserializationConfig.with(vc);
-        _serializationConfig = _serializationConfig.with(vc);
+        _configOverrides.setDefaultVisibility(vc);
         return this;
     }
-    
+
     /**
      * Convenience method that allows changing configuration for
      * underlying {@link VisibilityChecker}s, to change details of what kinds of
@@ -1264,11 +1277,12 @@
      */
     public ObjectMapper setVisibility(PropertyAccessor forMethod, JsonAutoDetect.Visibility visibility)
     {
-        _deserializationConfig = _deserializationConfig.withVisibility(forMethod, visibility);
-        _serializationConfig = _serializationConfig.withVisibility(forMethod, visibility);
+        VisibilityChecker<?> vc = _configOverrides.getDefaultVisibility();
+        vc = vc.withVisibility(forMethod, visibility);
+        _configOverrides.setDefaultVisibility(vc);
         return this;
     }
-    
+
     /**
      * Method for accessing subtype resolver in use.
      */
@@ -1342,27 +1356,6 @@
     }
 
     /**
-     * Convenience method, equivalent to calling:
-     *<pre>
-     *  setPropertyInclusion(JsonInclude.Value.construct(incl, Include.ALWAYS));
-     *</pre>
-     */
-    public ObjectMapper setSerializationInclusion(JsonInclude.Include incl) {
-        setPropertyInclusion(JsonInclude.Value.construct(incl, JsonInclude.Include.USE_DEFAULTS));
-        return this;
-    }
-
-    /**
-     * Method for setting default POJO property inclusion strategy for serialization.
-     *
-     * @since 2.7
-     */
-    public ObjectMapper setPropertyInclusion(JsonInclude.Value incl) {
-        _serializationConfig = _serializationConfig.withPropertyInclusion(incl);
-        return this;
-    }
-
-    /**
      * Method for specifying {@link PrettyPrinter} to use when "default pretty-printing"
      * is enabled (by enabling {@link SerializationFeature#INDENT_OUTPUT})
      * 
@@ -1377,6 +1370,105 @@
         return this;
     }
 
+    /**
+     * @deprecated Since 2.6 use {@link #setVisibility(VisibilityChecker)} instead.
+     */
+    @Deprecated
+    public void setVisibilityChecker(VisibilityChecker<?> vc) {
+        setVisibility(vc);
+    }
+
+    /*
+    /**********************************************************
+    /* Configuration: global-default/per-type override settings
+    /**********************************************************
+     */
+    
+    /**
+     * Convenience method, equivalent to calling:
+     *<pre>
+     *  setPropertyInclusion(JsonInclude.Value.construct(incl, incl));
+     *</pre>
+     *<p>
+     * NOTE: behavior differs slightly from 2.8, where second argument was
+     * implied to be <code>JsonInclude.Include.ALWAYS</code>.
+     */
+    public ObjectMapper setSerializationInclusion(JsonInclude.Include incl) {
+        setPropertyInclusion(JsonInclude.Value.construct(incl, incl));
+        return this;
+    }
+
+    /**
+     * @since 2.7
+     * @deprecated Since 2.9 use {@link #setDefaultPropertyInclusion}
+     */
+    @Deprecated
+    public ObjectMapper setPropertyInclusion(JsonInclude.Value incl) {
+        return setDefaultPropertyInclusion(incl);
+    }
+
+    /**
+     * Method for setting default POJO property inclusion strategy for serialization,
+     * applied for all properties for which there are no per-type or per-property
+     * overrides (via annotations or config overrides).
+     *
+     * @since 2.9 (basically rename of <code>setPropertyInclusion</code>)
+     */
+    public ObjectMapper setDefaultPropertyInclusion(JsonInclude.Value incl) {
+        _configOverrides.setDefaultInclusion(incl);
+        return this;
+    }
+
+    /**
+     * Short-cut for:
+     *<pre>
+     *  setDefaultPropertyInclusion(JsonInclude.Value.construct(incl, incl));
+     *</pre>
+     *
+     * @since 2.9 (basically rename of <code>setPropertyInclusion</code>)
+     */
+    public ObjectMapper setDefaultPropertyInclusion(JsonInclude.Include incl) {
+        _configOverrides.setDefaultInclusion(JsonInclude.Value.construct(incl, incl));
+        return this;
+    }
+
+    /**
+     * Method for setting default Setter configuration, regarding things like
+     * merging, null-handling; used for properties for which there are
+     * no per-type or per-property overrides (via annotations or config overrides).
+     *
+     * @since 2.9
+     */
+    public ObjectMapper setDefaultSetterInfo(JsonSetter.Value v) {
+        _configOverrides.setDefaultSetterInfo(v);
+        return this;
+    }
+
+    /**
+     * Method for setting auto-detection visibility definition
+     * defaults, which are in effect unless overridden by
+     * annotations (like <code>JsonAutoDetect</code>) or per-type
+     * visibility overrides.
+     *
+     * @since 2.9
+     */
+    public ObjectMapper setDefaultVisibility(JsonAutoDetect.Value vis) {
+        _configOverrides.setDefaultVisibility(VisibilityChecker.Std.construct(vis));
+        return this;
+    }
+
+    /**
+     * Method for setting default Setter configuration, regarding things like
+     * merging, null-handling; used for properties for which there are
+     * no per-type or per-property overrides (via annotations or config overrides).
+     *
+     * @since 2.9
+     */
+    public ObjectMapper setDefaultMergeable(Boolean b) {
+        _configOverrides.setDefaultMergeable(b);
+        return this;
+    }
+
     /*
     /**********************************************************
     /* Type information configuration
@@ -1386,8 +1478,14 @@
     /**
      * Convenience method that is equivalent to calling
      *<pre>
-     *  enableObjectTyping(DefaultTyping.OBJECT_AND_NON_CONCRETE);
+     *  enableDefaultTyping(DefaultTyping.OBJECT_AND_NON_CONCRETE);
      *</pre>
+     *<p>
+     * NOTE: use of Default Typing can be a potential security risk if incoming
+     * content comes from untrusted sources, and it is recommended that this
+     * is either not done, or, if enabled, use {@link #setDefaultTyping}
+     * passing a custom {@link TypeResolverBuilder} implementation that white-lists
+     * legal types to use.
      */
     public ObjectMapper enableDefaultTyping() {
         return enableDefaultTyping(DefaultTyping.OBJECT_AND_NON_CONCRETE);
@@ -1396,8 +1494,14 @@
     /**
      * Convenience method that is equivalent to calling
      *<pre>
-     *  enableObjectTyping(dti, JsonTypeInfo.As.WRAPPER_ARRAY);
+     *  enableDefaultTyping(dti, JsonTypeInfo.As.WRAPPER_ARRAY);
      *</pre>
+     *<p>
+     * NOTE: use of Default Typing can be a potential security risk if incoming
+     * content comes from untrusted sources, and it is recommended that this
+     * is either not done, or, if enabled, use {@link #setDefaultTyping}
+     * passing a custom {@link TypeResolverBuilder} implementation that white-lists
+     * legal types to use.
      */
     public ObjectMapper enableDefaultTyping(DefaultTyping dti) {
         return enableDefaultTyping(dti, JsonTypeInfo.As.WRAPPER_ARRAY);
@@ -1411,6 +1515,12 @@
      * NOTE: use of <code>JsonTypeInfo.As#EXTERNAL_PROPERTY</code> <b>NOT SUPPORTED</b>;
      * and attempts of do so will throw an {@link IllegalArgumentException} to make
      * this limitation explicit.
+     *<p>
+     * NOTE: use of Default Typing can be a potential security risk if incoming
+     * content comes from untrusted sources, and it is recommended that this
+     * is either not done, or, if enabled, use {@link #setDefaultTyping}
+     * passing a custom {@link TypeResolverBuilder} implementation that white-lists
+     * legal types to use.
      * 
      * @param applicability Defines kinds of types for which additional type information
      *    is added; see {@link DefaultTyping} for more information.
@@ -1421,7 +1531,7 @@
          *   use "As.EXTERNAL_PROPERTY", since that will not work (with 2.5+)
          */
         if (includeAs == JsonTypeInfo.As.EXTERNAL_PROPERTY) {
-            throw new IllegalArgumentException("Can not use includeAs of "+includeAs);
+            throw new IllegalArgumentException("Cannot use includeAs of "+includeAs);
         }
         
         TypeResolverBuilder<?> typer = new DefaultTypeResolverBuilder(applicability);
@@ -1438,6 +1548,12 @@
      * using "As.PROPERTY" inclusion mechanism and specified property name
      * to use for inclusion (default being "@class" since default type information
      * always uses class name as type identifier)
+     *<p>
+     * NOTE: use of Default Typing can be a potential security risk if incoming
+     * content comes from untrusted sources, and it is recommended that this
+     * is either not done, or, if enabled, use {@link #setDefaultTyping}
+     * passing a custom {@link TypeResolverBuilder} implementation that white-lists
+     * legal types to use.
      */
     public ObjectMapper enableDefaultTypingAsProperty(DefaultTyping applicability, String propertyName)
     {
@@ -1448,7 +1564,7 @@
         typer = typer.typeProperty(propertyName);
         return setDefaultTyping(typer);
     }
-    
+
     /**
      * Method for disabling automatic inclusion of type information; if so, only
      * explicitly annotated types (ones with
@@ -1463,6 +1579,11 @@
      * Method for enabling automatic inclusion of type information, using
      * specified handler object for determining which types this affects,
      * as well as details of how information is embedded.
+     *<p>
+     * NOTE: use of Default Typing can be a potential security risk if incoming
+     * content comes from untrusted sources, so care should be taken to use
+     * a {@link TypeResolverBuilder} that can limit allowed classes to
+     * deserialize.
      * 
      * @param typer Type information inclusion handler
      */
@@ -1495,6 +1616,13 @@
         getSubtypeResolver().registerSubtypes(types);
     }
 
+    /**
+     * @since 2.9
+     */
+    public void registerSubtypes(Collection<Class<?>> subtypes) {
+        getSubtypeResolver().registerSubtypes(subtypes);
+    }
+
     /*
     /**********************************************************
     /* Configuration, basic type handling
@@ -1519,7 +1647,7 @@
      * @since 2.8
      */
     public MutableConfigOverride configOverride(Class<?> type) {
-        return _propertyOverrides.findOrCreateOverride(type);
+        return _configOverrides.findOrCreateOverride(type);
     }
 
     /*
@@ -1549,7 +1677,7 @@
         _serializationConfig = _serializationConfig.with(f);
         return this;
     }
-    
+
     /**
      * Convenience method for constructing {@link JavaType} out of given
      * type (typically <code>java.lang.Class</code>), but without explicit
@@ -1558,7 +1686,7 @@
     public JavaType constructType(Type t) {
         return _typeFactory.constructType(t);
     }
-    
+
     /*
     /**********************************************************
     /* Configuration, deserialization
@@ -1578,7 +1706,7 @@
     public JsonNodeFactory getNodeFactory() {
         return _deserializationConfig.getNodeFactory();
     }
-    
+
     /**
      * Method for specifying {@link JsonNodeFactory} to use for
      * constructing root level tree nodes (via method
@@ -1701,6 +1829,15 @@
      * Method that can be used to get hold of {@link JsonFactory} that this
      * mapper uses if it needs to construct {@link JsonParser}s
      * and/or {@link JsonGenerator}s.
+     *<p>
+     * WARNING: note that all {@link ObjectReader} and {@link ObjectWriter}
+     * instances created by this mapper usually share the same configured
+     * {@link JsonFactory}, so changes to its configuration will "leak".
+     * To avoid such observed changes you should always use "with()" and
+     * "without()" method of {@link ObjectReader} and {@link ObjectWriter}
+     * for changing {@link com.fasterxml.jackson.core.JsonParser.Feature}
+     * and {@link com.fasterxml.jackson.core.JsonGenerator.Feature}
+     * settings to use on per-call basis.
      *
      * @return {@link JsonFactory} that this mapper uses when it needs to
      *   construct Json parser and generators
@@ -1789,7 +1926,7 @@
         _serializationConfig = _serializationConfig.with(tz);
         return this;
     }
-    
+
     /*
     /**********************************************************
     /* Configuration, simple features: MapperFeature
@@ -1976,6 +2113,10 @@
      *<p>
      * Note that this is equivalent to directly calling same method
      * on {@link #getFactory}.
+     *<p>
+     * WARNING: since this method directly modifies state of underlying {@link JsonFactory},
+     * it will change observed configuration by {@link ObjectReader}s as well -- to avoid
+     * this, use {@link ObjectReader#with(JsonParser.Feature)} instead.
      */
     public ObjectMapper configure(JsonParser.Feature f, boolean state) {
         _jsonFactory.configure(f, state);
@@ -1987,6 +2128,10 @@
      * for parser instances this object mapper creates.
      *<p>
      * Note that this is equivalent to directly calling same method on {@link #getFactory}.
+     *<p>
+     * WARNING: since this method directly modifies state of underlying {@link JsonFactory},
+     * it will change observed configuration by {@link ObjectReader}s as well -- to avoid
+     * this, use {@link ObjectReader#with(JsonParser.Feature)} instead.
      *
      * @since 2.5
      */
@@ -2002,6 +2147,10 @@
      * for parser instances this object mapper creates.
      *<p>
      * Note that this is equivalent to directly calling same method on {@link #getFactory}.
+     *<p>
+     * WARNING: since this method directly modifies state of underlying {@link JsonFactory},
+     * it will change observed configuration by {@link ObjectReader}s as well -- to avoid
+     * this, use {@link ObjectReader#without(JsonParser.Feature)} instead.
      *
      * @since 2.5
      */
@@ -2028,6 +2177,10 @@
      *<p>
      * Note that this is equivalent to directly calling same method
      * on {@link #getFactory}.
+     *<p>
+     * WARNING: since this method directly modifies state of underlying {@link JsonFactory},
+     * it will change observed configuration by {@link ObjectWriter}s as well -- to avoid
+     * this, use {@link ObjectWriter#with(JsonGenerator.Feature)} instead.
      */
     public ObjectMapper configure(JsonGenerator.Feature f, boolean state) {
         _jsonFactory.configure(f,  state);
@@ -2039,6 +2192,10 @@
      * for parser instances this object mapper creates.
      *<p>
      * Note that this is equivalent to directly calling same method on {@link #getFactory}.
+     *<p>
+     * WARNING: since this method directly modifies state of underlying {@link JsonFactory},
+     * it will change observed configuration by {@link ObjectWriter}s as well -- to avoid
+     * this, use {@link ObjectWriter#with(JsonGenerator.Feature)} instead.
      *
      * @since 2.5
      */
@@ -2054,6 +2211,10 @@
      * for parser instances this object mapper creates.
      *<p>
      * Note that this is equivalent to directly calling same method on {@link #getFactory}.
+     *<p>
+     * WARNING: since this method directly modifies state of underlying {@link JsonFactory},
+     * it will change observed configuration by {@link ObjectWriter}s as well -- to avoid
+     * this, use {@link ObjectWriter#without(JsonGenerator.Feature)} instead.
      *
      * @since 2.5
      */
@@ -2096,7 +2257,7 @@
      * Note: this method should NOT be used if the result type is a
      * container ({@link java.util.Collection} or {@link java.util.Map}.
      * The reason is that due to type erasure, key and value types
-     * can not be introspected when using this method.
+     * cannot be introspected when using this method.
      * 
      * @throws IOException if a low-level I/O problem (unexpected end-of-input,
      *   network error) occurs (passed through as-is without additional wrapping -- note
@@ -2331,11 +2492,9 @@
      * @throws JsonParseException if underlying input contains invalid content
      *    of type {@link JsonParser} supports (JSON for default case)
      */
-    public JsonNode readTree(InputStream in)
-        throws IOException, JsonProcessingException
+    public JsonNode readTree(InputStream in) throws IOException
     {
-        JsonNode n = (JsonNode) _readMapAndClose(_jsonFactory.createParser(in), JSON_NODE_TYPE);
-        return (n == null) ? NullNode.instance : n;
+        return _readTreeAndClose(_jsonFactory.createParser(in));
     }
 
     /**
@@ -2361,11 +2520,8 @@
      *   as a non-null {@link JsonNode} (one that returns <code>true</code>
      *   for {@link JsonNode#isNull()}
      */
-    public JsonNode readTree(Reader r)
-        throws IOException, JsonProcessingException
-    {
-        JsonNode n = (JsonNode) _readMapAndClose(_jsonFactory.createParser(r), JSON_NODE_TYPE);
-        return (n == null) ? NullNode.instance : n;
+    public JsonNode readTree(Reader r) throws IOException {
+        return _readTreeAndClose(_jsonFactory.createParser(r));
     }
 
     /**
@@ -2391,11 +2547,8 @@
      * @throws JsonParseException if underlying input contains invalid content
      *    of type {@link JsonParser} supports (JSON for default case)
      */
-    public JsonNode readTree(String content)
-        throws IOException, JsonProcessingException
-    {
-        JsonNode n = (JsonNode) _readMapAndClose(_jsonFactory.createParser(content), JSON_NODE_TYPE);
-        return (n == null) ? NullNode.instance : n;
+    public JsonNode readTree(String content) throws IOException {
+        return _readTreeAndClose(_jsonFactory.createParser(content));
     }
 
     /**
@@ -2414,11 +2567,8 @@
      * @throws JsonParseException if underlying input contains invalid content
      *    of type {@link JsonParser} supports (JSON for default case)
      */
-    public JsonNode readTree(byte[] content)
-        throws IOException, JsonProcessingException
-    {
-        JsonNode n = (JsonNode) _readMapAndClose(_jsonFactory.createParser(content), JSON_NODE_TYPE);
-        return (n == null) ? NullNode.instance : n;
+    public JsonNode readTree(byte[] content) throws IOException {
+        return _readTreeAndClose(_jsonFactory.createParser(content));
     }
     
     /**
@@ -2444,8 +2594,7 @@
     public JsonNode readTree(File file)
         throws IOException, JsonProcessingException
     {
-        JsonNode n = (JsonNode) _readMapAndClose(_jsonFactory.createParser(file), JSON_NODE_TYPE);
-        return (n == null) ? NullNode.instance : n;
+        return _readTreeAndClose(_jsonFactory.createParser(file));
     }
 
     /**
@@ -2468,11 +2617,8 @@
      * @throws JsonParseException if underlying input contains invalid content
      *    of type {@link JsonParser} supports (JSON for default case)
      */
-    public JsonNode readTree(URL source)
-        throws IOException, JsonProcessingException
-    {
-        JsonNode n = (JsonNode) _readMapAndClose(_jsonFactory.createParser(source), JSON_NODE_TYPE);
-        return (n == null) ? NullNode.instance : n;
+    public JsonNode readTree(URL source) throws IOException {
+        return _readTreeAndClose(_jsonFactory.createParser(source));
     }
 
     /*
@@ -2547,7 +2693,7 @@
     /**
      *<p>
      * Note: return type is co-variant, as basic ObjectCodec
-     * abstraction can not refer to concrete node types (as it's
+     * abstraction cannot refer to concrete node types (as it's
      * part of core package, whereas impls are part of mapper
      * package)
      */
@@ -2559,7 +2705,7 @@
     /**
      *<p>
      * Note: return type is co-variant, as basic ObjectCodec
-     * abstraction can not refer to concrete node types (as it's
+     * abstraction cannot refer to concrete node types (as it's
      * part of core package, whereas impls are part of mapper
      * package)
      */
@@ -3071,14 +3217,14 @@
         SegmentedStringWriter sw = new SegmentedStringWriter(_jsonFactory._getBufferRecycler());
         try {
             _configAndWriteValue(_jsonFactory.createGenerator(sw), value);
-        } catch (JsonProcessingException e) { // to support [JACKSON-758]
+        } catch (JsonProcessingException e) {
             throw e;
         } catch (IOException e) { // shouldn't really happen, but is declared as possibility so:
             throw JsonMappingException.fromUnexpectedIOE(e);
         }
         return sw.getAndClear();
     }
-    
+
     /**
      * Method that can be used to serialize any Java value as
      * a byte array. Functionally equivalent to calling
@@ -3519,8 +3665,6 @@
     public <T> T convertValue(Object fromValue, Class<T> toValueType)
         throws IllegalArgumentException
     {
-        // sanity check for null first:
-        if (fromValue == null) return null;
         return (T) _convert(fromValue, _typeFactory.constructType(toValueType));
     } 
 
@@ -3531,7 +3675,7 @@
     public <T> T convertValue(Object fromValue, TypeReference<?> toValueTypeRef)
         throws IllegalArgumentException
     {
-        return (T) convertValue(fromValue, _typeFactory.constructType(toValueTypeRef));
+        return (T) _convert(fromValue, _typeFactory.constructType(toValueTypeRef));
     } 
 
     /**
@@ -3541,8 +3685,6 @@
     public <T> T convertValue(Object fromValue, JavaType toValueType)
         throws IllegalArgumentException
     {
-        // sanity check for null first:
-        if (fromValue == null) return null;
         return (T) _convert(fromValue, toValueType);
     } 
 
@@ -3557,17 +3699,20 @@
     @SuppressWarnings("resource")
     protected Object _convert(Object fromValue, JavaType toValueType)
         throws IllegalArgumentException
-    {        
-        // also, as per [databind#11], consider case for simple cast
-        /* But with caveats: one is that while everything is Object.class, we don't
-         * want to "optimize" that out; and the other is that we also do not want
-         * to lose conversions of generic types.
-         */
-        Class<?> targetType = toValueType.getRawClass();
-        if (targetType != Object.class
-                && !toValueType.hasGenericTypes()
-                && targetType.isAssignableFrom(fromValue.getClass())) {
-            return fromValue;
+    {
+        // [databind#1433] Do not shortcut null values.
+        // This defaults primitives and fires deserializer getNullValue hooks.
+        if (fromValue != null) {
+            // also, as per [databind#11], consider case for simple cast
+            // But with caveats: one is that while everything is Object.class, we don't
+            // want to "optimize" that out; and the other is that we also do not want
+            // to lose conversions of generic types.
+            Class<?> targetType = toValueType.getRawClass();
+            if (targetType != Object.class
+                    && !toValueType.hasGenericTypes()
+                    && targetType.isAssignableFrom(fromValue.getClass())) {
+                return fromValue;
+            }
         }
         
         // Then use TokenBuffer, which is a JsonGenerator:
@@ -3587,7 +3732,7 @@
             Object result;
             // ok to pass in existing feature flags; unwrapping handled by mapper
             final DeserializationConfig deserConfig = getDeserializationConfig();
-            JsonToken t = _initForReading(p);
+            JsonToken t = _initForReading(p, toValueType);
             if (t == JsonToken.VALUE_NULL) {
                 DeserializationContext ctxt = createDeserializationContext(p, deserConfig);
                 result = _findRootDeserializer(ctxt, toValueType).getNullValue(ctxt);
@@ -3606,6 +3751,69 @@
         }
     }
 
+    /**
+     * Convenience method similar to {@link #convertValue(Object, JavaType)} but one
+     * in which 
+     *<p>
+     * Implementation is approximately as follows:
+     *<ol>
+     * <li>Serialize `updateWithValue` into {@link TokenBuffer}</li>
+     * <li>Construct {@link ObjectReader} with `valueToUpdate` (using {@link #readerForUpdating(Object)})
+     *   </li>
+     * <li>Construct {@link JsonParser} (using {@link TokenBuffer#asParser()})
+     *   </li>
+     * <li>Update using {@link ObjectReader#readValue(JsonParser)}.
+     *   </li>
+     * <li>Return `valueToUpdate`
+     *   </li>
+     *</ol>
+     *<p>
+     * Note that update is "shallow" in that only first level of properties (or, immediate contents
+     * of container to update) are modified, unless properties themselves indicate that
+     * merging should be applied for contents. Such merging can be specified using
+     * annotations (see <code>JsonMerge</code>) as well as using "config overrides" (see
+     * {@link #configOverride(Class)} and {@link #setDefaultMergeable(Boolean)}).
+     *
+     * @param valueToUpdate Object to update
+     * @param overrides Object to conceptually serialize and merge into value to
+     *     update; can be thought of as a provider for overrides to apply.
+     * 
+     * @return Either the first argument (`valueToUpdate`), if it is mutable; or a result of
+     *     creating new instance that is result of "merging" values (for example, "updating" a
+     *     Java array will create a new array)
+     *
+     * @throws JsonMappingException if there are structural incompatibilities that prevent update.
+     * 
+     * @since 2.9
+     */
+    @SuppressWarnings("resource")
+    public <T> T updateValue(T valueToUpdate, Object overrides)
+        throws JsonMappingException
+    {
+        T result = valueToUpdate;
+        if ((valueToUpdate != null) && (overrides != null)) {
+            TokenBuffer buf = new TokenBuffer(this, false);
+            if (isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) {
+                buf = buf.forceUseOfBigDecimal(true);
+            }
+            try {
+                SerializationConfig config = getSerializationConfig().
+                        without(SerializationFeature.WRAP_ROOT_VALUE);
+                _serializerProvider(config).serializeValue(buf, overrides);
+                JsonParser p = buf.asParser();
+                result = readerForUpdating(valueToUpdate).readValue(p);
+                p.close();
+            } catch (IOException e) { // should not occur, no real i/o...
+                if (e instanceof JsonMappingException) {
+                    throw (JsonMappingException) e;
+                }
+                // 17-Mar-2017, tatu: Really ought not happen...
+                throw JsonMappingException.fromUnexpectedIOE(e);
+            }
+        }
+        return result;
+    }
+
     /*
     /**********************************************************
     /* Extended Public API: JSON Schema generation
@@ -3644,7 +3852,7 @@
     {
         acceptJsonFormatVisitor(_typeFactory.constructType(type), visitor);
     }
-    
+
     /**
      * Method for visiting type hierarchy for given type, using specified visitor.
      * Visitation uses <code>Serializer</code> hierarchy and related properties
@@ -3665,7 +3873,7 @@
         }
         _serializerProvider(getSerializationConfig()).acceptJsonFormatVisitor(type, visitor);
     }
-    
+
     /*
     /**********************************************************
     /* Internal methods for serialization, overridable
@@ -3696,7 +3904,7 @@
         try {
             _serializerProvider(cfg).serializeValue(g, value);
         } catch (Exception e) {
-            ClassUtil.closeOnFailAndThrowAsIAE(g, e);
+            ClassUtil.closeOnFailAndThrowAsIOE(g, e);
             return;
         }
         g.close();
@@ -3716,7 +3924,7 @@
             toClose = null;
             tmpToClose.close();
         } catch (Exception e) {
-            ClassUtil.closeOnFailAndThrowAsIAE(g, toClose, e);
+            ClassUtil.closeOnFailAndThrowAsIOE(g, toClose, e);
             return;
         }
         g.close();
@@ -3736,7 +3944,7 @@
                 g.flush();
             }
         } catch (Exception e) {
-            ClassUtil.closeOnFailAndThrowAsIAE(null, toClose, e);
+            ClassUtil.closeOnFailAndThrowAsIOE(null, toClose, e);
             return;
         }
         toClose.close();
@@ -3749,19 +3957,10 @@
      */
 
     /**
-     * Internal helper method called to create an instance of {@link DeserializationContext}
-     * for deserializing a single root value.
-     * Can be overridden if a custom context is needed.
-     */
-    protected DefaultDeserializationContext createDeserializationContext(JsonParser p,
-            DeserializationConfig cfg) {
-        return _deserializationContext.createInstance(cfg, p, _injectableValues);
-    }
-    
-    /**
      * Actual implementation of value reading+binding operation.
      */
-    protected Object _readValue(DeserializationConfig cfg, JsonParser p, JavaType valueType)
+    protected Object _readValue(DeserializationConfig cfg, JsonParser p,
+            JavaType valueType)
         throws IOException
     {
         /* First: may need to read the next token, to initialize
@@ -3769,15 +3968,14 @@
          * previous token has been cleared)
          */
         Object result;
-        JsonToken t = _initForReading(p);
+        JsonToken t = _initForReading(p, valueType);
+        final DeserializationContext ctxt = createDeserializationContext(p, cfg);
         if (t == JsonToken.VALUE_NULL) {
             // Ask JsonDeserializer what 'null value' to use:
-            DeserializationContext ctxt = createDeserializationContext(p, cfg);
             result = _findRootDeserializer(ctxt, valueType).getNullValue(ctxt);
         } else if (t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT) {
             result = null;
         } else { // pointing to event other than null
-            DeserializationContext ctxt = createDeserializationContext(p, cfg);
             JsonDeserializer<Object> deser = _findRootDeserializer(ctxt, valueType);
             // ok, let's get the value
             if (cfg.useRootWrapping()) {
@@ -3788,25 +3986,26 @@
         }
         // Need to consume the token too
         p.clearCurrentToken();
+        if (cfg.isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)) {
+            _verifyNoTrailingTokens(p, ctxt, valueType);
+        }
         return result;
     }
-    
+
     protected Object _readMapAndClose(JsonParser p0, JavaType valueType)
         throws IOException
     {
         try (JsonParser p = p0) {
             Object result;
-            JsonToken t = _initForReading(p);
+            JsonToken t = _initForReading(p, valueType);
+            final DeserializationConfig cfg = getDeserializationConfig();
+            final DeserializationContext ctxt = createDeserializationContext(p, cfg);
             if (t == JsonToken.VALUE_NULL) {
                 // Ask JsonDeserializer what 'null value' to use:
-                DeserializationContext ctxt = createDeserializationContext(p,
-                        getDeserializationConfig());
                 result = _findRootDeserializer(ctxt, valueType).getNullValue(ctxt);
             } else if (t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT) {
                 result = null;
             } else {
-                DeserializationConfig cfg = getDeserializationConfig();
-                DeserializationContext ctxt = createDeserializationContext(p, cfg);
                 JsonDeserializer<Object> deser = _findRootDeserializer(ctxt, valueType);
                 if (cfg.useRootWrapping()) {
                     result = _unwrapAndDeserialize(p, ctxt, cfg, valueType, deser);
@@ -3815,46 +4014,54 @@
                 }
                 ctxt.checkUnresolvedObjectId();
             }
-            // Need to consume the token too
-            p.clearCurrentToken();
+            if (cfg.isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)) {
+                _verifyNoTrailingTokens(p, ctxt, valueType);
+            }
             return result;
         }
     }
 
     /**
-     * Method called to ensure that given parser is ready for reading
-     * content for data binding.
+     * Similar to {@link #_readMapAndClose} but specialized for <code>JsonNode</code>
+     * reading.
      *
-     * @return First token to be used for data binding after this call:
-     *  can never be null as exception will be thrown if parser can not
-     *  provide more tokens.
-     *
-     * @throws IOException if the underlying input source has problems during
-     *   parsing
-     * @throws JsonParseException if parser has problems parsing content
-     * @throws JsonMappingException if the parser does not have any more
-     *   content to map (note: Json "null" value is considered content;
-     *   enf-of-stream not)
+     * @since 2.9
      */
-    protected JsonToken _initForReading(JsonParser p) throws IOException
+    protected JsonNode _readTreeAndClose(JsonParser p0) throws IOException
     {
-        _deserializationConfig.initialize(p); // since 2.5
+        try (JsonParser p = p0) {
+            final JavaType valueType = JSON_NODE_TYPE;
 
-        /* First: must point to a token; if not pointing to one, advance.
-         * This occurs before first read from JsonParser, as well as
-         * after clearing of current token.
-         */
-        JsonToken t = p.getCurrentToken();
-        if (t == null) {
-            // and then we must get something...
-            t = p.nextToken();
+            DeserializationConfig cfg = getDeserializationConfig();
+            // 27-Oct-2016, tatu: Need to inline `_initForReading()` due to
+            //   special requirements by tree reading (no fail on eof)
+            
+            cfg.initialize(p); // since 2.5
+            JsonToken t = p.getCurrentToken();
             if (t == null) {
-                // Throw mapping exception, since it's failure to map,
-                //   not an actual parsing problem
-                throw JsonMappingException.from(p, "No content to map due to end-of-input");
+                t = p.nextToken();
+                if (t == null) { // [databind#1406]: expose end-of-input as `null`
+                    return null;
+                }
             }
+            if (t == JsonToken.VALUE_NULL) {
+                return cfg.getNodeFactory().nullNode();
+            }
+            DeserializationContext ctxt = createDeserializationContext(p, cfg);
+            JsonDeserializer<Object> deser = _findRootDeserializer(ctxt, valueType);
+            Object result;
+            if (cfg.useRootWrapping()) {
+                result = _unwrapAndDeserialize(p, ctxt, cfg, valueType, deser);
+            } else {
+                result = deser.deserialize(p, ctxt);
+                if (cfg.isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)) {
+                    _verifyNoTrailingTokens(p, ctxt, valueType);
+                }
+            }
+            // No ObjectIds so can ignore
+//            ctxt.checkUnresolvedObjectId();
+            return (JsonNode) result;
         }
-        return t;
     }
 
     protected Object _unwrapAndDeserialize(JsonParser p, DeserializationContext ctxt, 
@@ -3866,19 +4073,19 @@
         // 12-Jun-2015, tatu: Should try to support namespaces etc but...
         String expSimpleName = expRootName.getSimpleName();
         if (p.getCurrentToken() != JsonToken.START_OBJECT) {
-            ctxt.reportWrongTokenException(p, JsonToken.START_OBJECT,
+            ctxt.reportWrongTokenException(rootType, JsonToken.START_OBJECT,
                     "Current token not START_OBJECT (needed to unwrap root name '%s'), but %s",
                     expSimpleName, p.getCurrentToken());
-            
         }
         if (p.nextToken() != JsonToken.FIELD_NAME) {
-            ctxt.reportWrongTokenException(p, JsonToken.FIELD_NAME,
-                    "Current token not FIELD_NAME (to contain expected root name '"
-                    +expSimpleName+"'), but "+p.getCurrentToken());
+            ctxt.reportWrongTokenException(rootType, JsonToken.FIELD_NAME,
+                    "Current token not FIELD_NAME (to contain expected root name '%s'), but %s",
+                    expSimpleName, p.getCurrentToken());
         }
         String actualName = p.getCurrentName();
         if (!expSimpleName.equals(actualName)) {
-            ctxt.reportMappingException("Root name '%s' does not match expected ('%s') for type %s",
+            ctxt.reportInputMismatch(rootType,
+                    "Root name '%s' does not match expected ('%s') for type %s",
                     actualName, expSimpleName, rootType);
         }
         // ok, then move to value itself....
@@ -3886,13 +4093,81 @@
         Object result = deser.deserialize(p, ctxt);
         // and last, verify that we now get matching END_OBJECT
         if (p.nextToken() != JsonToken.END_OBJECT) {
-            ctxt.reportWrongTokenException(p, JsonToken.END_OBJECT,
+            ctxt.reportWrongTokenException(rootType, JsonToken.END_OBJECT,
                     "Current token not END_OBJECT (to match wrapper object with root name '%s'), but %s",
                     expSimpleName, p.getCurrentToken());
         }
+        if (config.isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)) {
+            _verifyNoTrailingTokens(p, ctxt, rootType);
+        }
         return result;
     }
-    
+
+    /**
+     * Internal helper method called to create an instance of {@link DeserializationContext}
+     * for deserializing a single root value.
+     * Can be overridden if a custom context is needed.
+     */
+    protected DefaultDeserializationContext createDeserializationContext(JsonParser p,
+            DeserializationConfig cfg) {
+        return _deserializationContext.createInstance(cfg, p, _injectableValues);
+    }
+
+    /**
+     * Method called to ensure that given parser is ready for reading
+     * content for data binding.
+     *
+     * @return First token to be used for data binding after this call:
+     *  can never be null as exception will be thrown if parser cannot
+     *  provide more tokens.
+     *
+     * @throws IOException if the underlying input source has problems during
+     *   parsing
+     * @throws JsonParseException if parser has problems parsing content
+     * @throws JsonMappingException if the parser does not have any more
+     *   content to map (note: Json "null" value is considered content;
+     *   enf-of-stream not)
+     */
+    protected JsonToken _initForReading(JsonParser p, JavaType targetType) throws IOException
+    {
+        _deserializationConfig.initialize(p); // since 2.5
+
+        // First: must point to a token; if not pointing to one, advance.
+        // This occurs before first read from JsonParser, as well as
+        // after clearing of current token.
+        JsonToken t = p.getCurrentToken();
+        if (t == null) {
+            // and then we must get something...
+            t = p.nextToken();
+            if (t == null) {
+                // Throw mapping exception, since it's failure to map,
+                //   not an actual parsing problem
+                throw MismatchedInputException.from(p, targetType,
+                        "No content to map due to end-of-input");
+            }
+        }
+        return t;
+    }
+
+    @Deprecated // since 2.9, use method that takes JavaType too
+    protected JsonToken _initForReading(JsonParser p) throws IOException {
+        return _initForReading(p, null);
+    }
+
+    /**
+     * @since 2.9
+     */
+    protected final void _verifyNoTrailingTokens(JsonParser p, DeserializationContext ctxt,
+            JavaType bindType)
+        throws IOException
+    {
+        JsonToken t = p.nextToken();
+        if (t != null) {
+            Class<?> bt = ClassUtil.rawClass(bindType);
+            ctxt.reportTrailingTokens(bt, p, t);
+        }
+    }
+
     /*
     /**********************************************************
     /* Internal methods, other
@@ -3914,8 +4189,8 @@
         // Nope: need to ask provider to resolve it
         deser = ctxt.findRootValueDeserializer(valueType);
         if (deser == null) { // can this happen?
-            throw JsonMappingException.from(ctxt,
-                    "Can not find a deserializer for type "+valueType);
+            return ctxt.reportBadDefinition(valueType,
+                    "Cannot find a deserializer for type "+valueType);
         }
         _rootDeserializers.put(valueType, deser);
         return deser;
@@ -3928,7 +4203,7 @@
     {
         if (schema != null) {
             if (!_jsonFactory.canUseSchema(schema)) {
-                    throw new IllegalArgumentException("Can not use FormatSchema of type "+schema.getClass().getName()
+                    throw new IllegalArgumentException("Cannot use FormatSchema of type "+schema.getClass().getName()
                             +" for format "+_jsonFactory.getFormatName());
             }
         }
diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java b/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java
index d1ec95f..d313c42 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java
@@ -11,15 +11,16 @@
 import com.fasterxml.jackson.core.filter.TokenFilter;
 import com.fasterxml.jackson.core.type.ResolvedType;
 import com.fasterxml.jackson.core.type.TypeReference;
+
 import com.fasterxml.jackson.databind.cfg.ContextAttributes;
 import com.fasterxml.jackson.databind.deser.DataFormatReaders;
 import com.fasterxml.jackson.databind.deser.DefaultDeserializationContext;
 import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler;
 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
-import com.fasterxml.jackson.databind.node.NullNode;
 import com.fasterxml.jackson.databind.node.TreeTraversingParser;
 import com.fasterxml.jackson.databind.type.SimpleType;
 import com.fasterxml.jackson.databind.type.TypeFactory;
+import com.fasterxml.jackson.databind.util.ClassUtil;
 
 /**
  * Builder object that can be used for per-serialization configuration of
@@ -32,12 +33,18 @@
  * Instances are initially constructed by {@link ObjectMapper} and can be
  * reused, shared, cached; both because of thread-safety and because
  * instances are relatively light-weight.
+ *<p>
+ * NOTE: this class is NOT meant as sub-classable (with Jackson 2.8 and
+ * above) by users. It is left as non-final mostly to allow frameworks
+ * that require bytecode generation for proxying and similar use cases,
+ * but there is no expecation that functionality should be extended
+ * by sub-classing.
  */
 public class ObjectReader
     extends ObjectCodec
     implements Versioned, java.io.Serializable // since 2.1
 {
-    private static final long serialVersionUID = 1L; // since 2.5
+    private static final long serialVersionUID = 2L; // since 2.9
 
     private final static JavaType JSON_NODE_TYPE = SimpleType.constructUnsafe(JsonNode.class);
 
@@ -107,7 +114,7 @@
      * this value object will be updated instead.
      * Note that value can be of almost any type, except not
      * {@link com.fasterxml.jackson.databind.type.ArrayType}; array
-     * types can not be modified because array size is immutable.
+     * types cannot be modified because array size is immutable.
      */
     protected final Object _valueToUpdate;
 
@@ -129,7 +136,7 @@
      * NOTE: If defined non-null, <code>readValue()</code> methods that take
      * {@link Reader} or {@link String} input <b>will fail with exception</b>,
      * because format-detection only works on byte-sources. Also, if format
-     * can not be detect reliably (as per detector settings),
+     * cannot be detect reliably (as per detector settings),
      * a {@link JsonParseException} will be thrown).
      * 
      * @since 2.1
@@ -175,9 +182,6 @@
         _parserFactory = mapper._jsonFactory;
         _valueType = valueType;
         _valueToUpdate = valueToUpdate;
-        if (valueToUpdate != null && valueType.isArrayType()) {
-            throw new IllegalArgumentException("Can not update an array value");
-        }
         _schema = schema;
         _injectableValues = injectableValues;
         _unwrapRoot = config.useRootWrapping();
@@ -204,9 +208,6 @@
         _valueType = valueType;
         _rootDeserializer = rootDeser;
         _valueToUpdate = valueToUpdate;
-        if (valueToUpdate != null && valueType.isArrayType()) {
-            throw new IllegalArgumentException("Can not update an array value");
-        }
         _schema = schema;
         _injectableValues = injectableValues;
         _unwrapRoot = config.useRootWrapping();
@@ -281,9 +282,9 @@
 
     /*
     /**********************************************************
-    /* Methods sub-classes MUST override, used for constructing
-    /* reader instances, (re)configuring parser instances
-    /* Added in 2.5
+    /* Helper methods used internally for invoking constructors
+    /* Need to be overridden if sub-classing (not recommended)
+    /* is used.
     /**********************************************************
      */
 
@@ -333,8 +334,7 @@
 
     /*
     /**********************************************************
-    /* Methods sub-classes may choose to override, if customized
-    /* initialization is needed.
+    /* Methods for initializing parser instance to use
     /**********************************************************
      */
 
@@ -355,7 +355,8 @@
             t = p.nextToken();
             if (t == null) {
                 // Throw mapping exception, since it's failure to map, not an actual parsing problem
-                ctxt.reportMissingContent(null); // default msg is fine
+                ctxt.reportInputMismatch(_valueType,
+                        "No content to map due to end-of-input");
             }
         }
         return t;
@@ -808,7 +809,7 @@
      * to construct appropriate {@link JsonParser} for actual parsing.
      *<p>
      * Note: since format detection only works with byte sources, it is possible to
-     * get a failure from some 'readValue()' methods. Also, if input can not be reliably
+     * get a failure from some 'readValue()' methods. Also, if input cannot be reliably
      * (enough) detected as one of specified types, an exception will be thrown.
      *<p>
      * Note: not all {@link JsonFactory} types can be passed: specifically, ones that
@@ -832,7 +833,7 @@
      * {@link DataFormatReaders}.
      *<p>
      * NOTE: since format detection only works with byte sources, it is possible to
-     * get a failure from some 'readValue()' methods. Also, if input can not be reliably
+     * get a failure from some 'readValue()' methods. Also, if input cannot be reliably
      * (enough) detected as one of specified types, an exception will be thrown.
      * 
      * @param readers DataFormatReaders to use for detecting underlying format.
@@ -1010,7 +1011,7 @@
      */
     @Override
     @SuppressWarnings("unchecked")
-    public <T> T readValue(JsonParser p, ResolvedType valueType) throws IOException, JsonProcessingException {
+    public <T> T readValue(JsonParser p, ResolvedType valueType) throws IOException {
         return (T) forType((JavaType)valueType).readValue(p);
     }
 
@@ -1138,7 +1139,10 @@
 
     @Override
     public JsonParser treeAsTokens(TreeNode n) {
-        return new TreeTraversingParser((JsonNode) n, this);
+        // 05-Dec-2017, tatu: Important! Must clear "valueToUpdate" since we do not
+        //    want update to be applied here, as a side effect
+        ObjectReader codec = withValueToUpdate(null);
+        return new TreeTraversingParser((JsonNode) n, codec);
     }
 
     /**
@@ -1159,7 +1163,7 @@
     }
      
     @Override
-    public void writeTree(JsonGenerator jgen, TreeNode rootNode) {
+    public void writeTree(JsonGenerator g, TreeNode rootNode) {
         throw new UnsupportedOperationException();
     }
     
@@ -1176,13 +1180,11 @@
      * was specified with {@link #withValueToUpdate(Object)}.
      */
     @SuppressWarnings("unchecked")
-    public <T> T readValue(InputStream src)
-        throws IOException, JsonProcessingException
+    public <T> T readValue(InputStream src) throws IOException
     {
         if (_dataFormatReaders != null) {
             return (T) _detectBindAndClose(_dataFormatReaders.findFormat(src), false);
         }
-        
         return (T) _bindAndClose(_considerFilter(_parserFactory.createParser(src), false));
     }
 
@@ -1193,16 +1195,14 @@
      * was specified with {@link #withValueToUpdate(Object)}.
      */
     @SuppressWarnings("unchecked")
-    public <T> T readValue(Reader src)
-        throws IOException, JsonProcessingException
+    public <T> T readValue(Reader src) throws IOException
     {
         if (_dataFormatReaders != null) {
             _reportUndetectableSource(src);
         }
-
         return (T) _bindAndClose(_considerFilter(_parserFactory.createParser(src), false));
     }
-    
+
     /**
      * Method that binds content read from given JSON string,
      * using configuration of this reader.
@@ -1210,8 +1210,7 @@
      * was specified with {@link #withValueToUpdate(Object)}.
      */
     @SuppressWarnings("unchecked")
-    public <T> T readValue(String src)
-        throws IOException, JsonProcessingException
+    public <T> T readValue(String src) throws IOException
     {
         if (_dataFormatReaders != null) {
             _reportUndetectableSource(src);
@@ -1227,13 +1226,11 @@
      * was specified with {@link #withValueToUpdate(Object)}.
      */
     @SuppressWarnings("unchecked")
-    public <T> T readValue(byte[] src)
-        throws IOException, JsonProcessingException
+    public <T> T readValue(byte[] src) throws IOException
     {
         if (_dataFormatReaders != null) {
             return (T) _detectBindAndClose(src, 0, src.length);
         }
-        
         return (T) _bindAndClose(_considerFilter(_parserFactory.createParser(src), false));
     }
 
@@ -1245,19 +1242,18 @@
      */
     @SuppressWarnings("unchecked")
     public <T> T readValue(byte[] src, int offset, int length)
-        throws IOException, JsonProcessingException
+        throws IOException
     {
         if (_dataFormatReaders != null) {
             return (T) _detectBindAndClose(src, offset, length);
         }
-
         return (T) _bindAndClose(_considerFilter(_parserFactory.createParser(src, offset, length),
                 false));
     }
     
     @SuppressWarnings("unchecked")
     public <T> T readValue(File src)
-        throws IOException, JsonProcessingException
+        throws IOException
     {
         if (_dataFormatReaders != null) {
             return (T) _detectBindAndClose(_dataFormatReaders.findFormat(_inputStream(src)), true);
@@ -1274,12 +1270,11 @@
      */
     @SuppressWarnings("unchecked")
     public <T> T readValue(URL src)
-        throws IOException, JsonProcessingException
+        throws IOException
     {
         if (_dataFormatReaders != null) {
             return (T) _detectBindAndClose(_dataFormatReaders.findFormat(_inputStream(src)), true);
         }
-
         return (T) _bindAndClose(_considerFilter(_parserFactory.createParser(src), false));
     }
 
@@ -1290,14 +1285,13 @@
      *   objectReader.readValue(src.traverse())
      *</pre>
      */
-    @SuppressWarnings("unchecked")
+    @SuppressWarnings({ "unchecked", "resource" })
     public <T> T readValue(JsonNode src)
-        throws IOException, JsonProcessingException
+        throws IOException
     {
         if (_dataFormatReaders != null) {
             _reportUndetectableSource(src);
         }
-        
         return (T) _bindAndClose(_considerFilter(treeAsTokens(src), false));
     }
 
@@ -1322,8 +1316,7 @@
      * it will just be ignored; result is always a newly constructed
      * {@link JsonNode} instance.
      */
-    public JsonNode readTree(InputStream in)
-        throws IOException, JsonProcessingException
+    public JsonNode readTree(InputStream in) throws IOException
     {
         if (_dataFormatReaders != null) {
             return _detectBindAndCloseAsTree(in);
@@ -1340,8 +1333,7 @@
      * it will just be ignored; result is always a newly constructed
      * {@link JsonNode} instance.
      */
-    public JsonNode readTree(Reader r)
-        throws IOException, JsonProcessingException
+    public JsonNode readTree(Reader r) throws IOException
     {
         if (_dataFormatReaders != null) {
             _reportUndetectableSource(r);
@@ -1358,8 +1350,7 @@
      * it will just be ignored; result is always a newly constructed
      * {@link JsonNode} instance.
      */
-    public JsonNode readTree(String json)
-        throws IOException, JsonProcessingException
+    public JsonNode readTree(String json) throws IOException
     {
         if (_dataFormatReaders != null) {
             _reportUndetectableSource(json);
@@ -1393,7 +1384,7 @@
      * to the token following it.
      */
     public <T> MappingIterator<T> readValues(JsonParser p)
-        throws IOException, JsonProcessingException
+        throws IOException
     {
         DeserializationContext ctxt = createDeserializationContext(p);
         // false -> do not close as caller gave parser instance
@@ -1421,7 +1412,7 @@
      * <code>START_ARRAY</code> which is part of the first element).
      */
     public <T> MappingIterator<T> readValues(InputStream src)
-        throws IOException, JsonProcessingException
+        throws IOException
     {
         if (_dataFormatReaders != null) {
             return _detectBindAndReadValues(_dataFormatReaders.findFormat(src), false);
@@ -1435,7 +1426,7 @@
      */
     @SuppressWarnings("resource")
     public <T> MappingIterator<T> readValues(Reader src)
-        throws IOException, JsonProcessingException
+        throws IOException
     {
         if (_dataFormatReaders != null) {
             _reportUndetectableSource(src);
@@ -1454,7 +1445,7 @@
      */
     @SuppressWarnings("resource")
     public <T> MappingIterator<T> readValues(String json)
-        throws IOException, JsonProcessingException
+        throws IOException
     {
         if (_dataFormatReaders != null) {
             _reportUndetectableSource(json);
@@ -1470,7 +1461,7 @@
      * Overloaded version of {@link #readValue(InputStream)}.
      */
     public <T> MappingIterator<T> readValues(byte[] src, int offset, int length)
-        throws IOException, JsonProcessingException
+        throws IOException
     {
         if (_dataFormatReaders != null) {
             return _detectBindAndReadValues(_dataFormatReaders.findFormat(src, offset, length), false);
@@ -1483,7 +1474,7 @@
      * Overloaded version of {@link #readValue(InputStream)}.
      */
     public final <T> MappingIterator<T> readValues(byte[] src)
-            throws IOException, JsonProcessingException {
+            throws IOException {
         return readValues(src, 0, src.length);
     }
     
@@ -1491,7 +1482,7 @@
      * Overloaded version of {@link #readValue(InputStream)}.
      */
     public <T> MappingIterator<T> readValues(File src)
-        throws IOException, JsonProcessingException
+        throws IOException
     {
         if (_dataFormatReaders != null) {
             return _detectBindAndReadValues(
@@ -1506,7 +1497,7 @@
      * @param src URL to read to access JSON content to parse.
      */
     public <T> MappingIterator<T> readValues(URL src)
-        throws IOException, JsonProcessingException
+        throws IOException
     {
         if (_dataFormatReaders != null) {
             return _detectBindAndReadValues(
@@ -1540,12 +1531,12 @@
         } catch (JsonProcessingException e) {
             throw e;
         } catch (IOException e) { // should not occur, no real i/o...
-            throw new IllegalArgumentException(e.getMessage(), e);
+            throw JsonMappingException.fromUnexpectedIOE(e);
         }
     }    
 
     @Override
-    public void writeValue(JsonGenerator gen, Object value) throws IOException, JsonProcessingException {
+    public void writeValue(JsonGenerator gen, Object value) throws IOException {
         throw new UnsupportedOperationException("Not implemented for ObjectReader");
     }
 
@@ -1554,7 +1545,7 @@
     /* Helper methods, data-binding
     /**********************************************************
      */
-    
+
     /**
      * Actual implementation of value reading+binding operation.
      */
@@ -1582,26 +1573,20 @@
                 if (valueToUpdate == null) {
                     result = deser.deserialize(p, ctxt);
                 } else {
-                    deser.deserialize(p, ctxt, valueToUpdate);
-                    result = valueToUpdate;
+                    // 20-Mar-2017, tatu: Important! May be different from `valueToUpdate`
+                    //   for immutable Objects like Java arrays; logical result
+                    result = deser.deserialize(p, ctxt, valueToUpdate);
                 }
             }
         }
         // Need to consume the token too
         p.clearCurrentToken();
+        if (_config.isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)) {
+            _verifyNoTrailingTokens(p, ctxt, _valueType);
+        }
         return result;
     }
-    
-    /**
-     * Consider filter when creating JsonParser.  
-     */
-    protected JsonParser _considerFilter(final JsonParser p, boolean multiValue) {
-        // 26-Mar-2016, tatu: Need to allow multiple-matches at least if we have
-        //    have a multiple-value read (that is, "readValues()").
-        return ((_filter == null) || FilteringParserDelegate.class.isInstance(p))
-                ? p : new FilteringParserDelegate(p, _filter, false, multiValue);
-    }
-    
+
     protected Object _bindAndClose(JsonParser p0) throws IOException
     {
         try (JsonParser p = p0) {
@@ -1630,36 +1615,53 @@
                     }
                 }
             }
+            if (_config.isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)) {
+                _verifyNoTrailingTokens(p, ctxt, _valueType);
+            }
             return result;
         }
     }
 
-    protected JsonNode _bindAndCloseAsTree(JsonParser p0) throws IOException {
+    protected final JsonNode _bindAndCloseAsTree(JsonParser p0) throws IOException {
         try (JsonParser p = p0) {
             return _bindAsTree(p);
         }
     }
-    
-    protected JsonNode _bindAsTree(JsonParser p) throws IOException
+
+    protected final JsonNode _bindAsTree(JsonParser p) throws IOException
     {
-        JsonNode result;
-        DeserializationContext ctxt = createDeserializationContext(p);
-        JsonToken t = _initForReading(ctxt, p);
-        if (t == JsonToken.VALUE_NULL || t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT) {
-            result = NullNode.instance;
-        } else {
-            JsonDeserializer<Object> deser = _findTreeDeserializer(ctxt);
-            if (_unwrapRoot) {
-                result = (JsonNode) _unwrapAndDeserialize(p, ctxt, JSON_NODE_TYPE, deser);
-            } else {
-                result = (JsonNode) deser.deserialize(p, ctxt);
+        // 27-Oct-2016, tatu: Need to inline `_initForReading()` due to
+        //   special requirements by tree reading (no fail on eof)
+        
+        _config.initialize(p);
+        if (_schema != null) {
+            p.setSchema(_schema);
+        }
+
+        JsonToken t = p.getCurrentToken();
+        if (t == null) {
+            t = p.nextToken();
+            if (t == null) { // [databind#1406]: expose end-of-input as `null`
+                return null;
             }
         }
-        // Need to consume the token too
-        p.clearCurrentToken();
-        return result;
+        DeserializationContext ctxt = createDeserializationContext(p);
+        if (t == JsonToken.VALUE_NULL) {
+            return ctxt.getNodeFactory().nullNode();
+        }
+        JsonDeserializer<Object> deser = _findTreeDeserializer(ctxt);
+        Object result;
+        if (_unwrapRoot) {
+            result = _unwrapAndDeserialize(p, ctxt, JSON_NODE_TYPE, deser);
+        } else {
+            result = deser.deserialize(p, ctxt);
+            if (_config.isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)) {
+                _verifyNoTrailingTokens(p, ctxt, JSON_NODE_TYPE);
+            }
+        }
+        return (JsonNode) result;
     }
-    
+
     /**
      * @since 2.1
      */
@@ -1679,18 +1681,19 @@
         String expSimpleName = expRootName.getSimpleName();
 
         if (p.getCurrentToken() != JsonToken.START_OBJECT) {
-            ctxt.reportWrongTokenException(p, JsonToken.START_OBJECT,
+            ctxt.reportWrongTokenException(rootType, JsonToken.START_OBJECT,
                     "Current token not START_OBJECT (needed to unwrap root name '%s'), but %s",
                     expSimpleName, p.getCurrentToken());
         }
         if (p.nextToken() != JsonToken.FIELD_NAME) {
-            ctxt.reportWrongTokenException(p, JsonToken.FIELD_NAME,
+            ctxt.reportWrongTokenException(rootType, JsonToken.FIELD_NAME,
                     "Current token not FIELD_NAME (to contain expected root name '%s'), but %s", 
                     expSimpleName, p.getCurrentToken());
         }
         String actualName = p.getCurrentName();
         if (!expSimpleName.equals(actualName)) {
-            ctxt.reportMappingException("Root name '%s' does not match expected ('%s') for type %s",
+            ctxt.reportInputMismatch(rootType,
+                    "Root name '%s' does not match expected ('%s') for type %s",
                     actualName, expSimpleName, rootType);
         }
         // ok, then move to value itself....
@@ -1704,13 +1707,45 @@
         }
         // and last, verify that we now get matching END_OBJECT
         if (p.nextToken() != JsonToken.END_OBJECT) {
-            ctxt.reportWrongTokenException(p, JsonToken.END_OBJECT,
+            ctxt.reportWrongTokenException(rootType, JsonToken.END_OBJECT,
                     "Current token not END_OBJECT (to match wrapper object with root name '%s'), but %s",
                     expSimpleName, p.getCurrentToken());
         }
+        if (_config.isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)) {
+            _verifyNoTrailingTokens(p, ctxt, _valueType);
+        }
         return result;
     }
 
+    /**
+     * Consider filter when creating JsonParser.  
+     */
+    protected JsonParser _considerFilter(final JsonParser p, boolean multiValue) {
+        // 26-Mar-2016, tatu: Need to allow multiple-matches at least if we have
+        //    have a multiple-value read (that is, "readValues()").
+        return ((_filter == null) || FilteringParserDelegate.class.isInstance(p))
+                ? p : new FilteringParserDelegate(p, _filter, false, multiValue);
+    }
+
+    /**
+     * @since 2.9
+     */
+    protected final void _verifyNoTrailingTokens(JsonParser p, DeserializationContext ctxt,
+            JavaType bindType)
+        throws IOException
+    {
+        JsonToken t = p.nextToken();
+        if (t != null) {
+            Class<?> bt = ClassUtil.rawClass(bindType);
+            if (bt == null) {
+                if (_valueToUpdate != null) {
+                    bt = _valueToUpdate.getClass();
+                }
+            }
+            ctxt.reportTrailingTokens(bt, p, t);
+        }
+    }
+
     /*
     /**********************************************************
     /* Internal methods, format auto-detection
@@ -1747,7 +1782,7 @@
 
     @SuppressWarnings("resource")
     protected <T> MappingIterator<T> _detectBindAndReadValues(DataFormatReaders.Match match, boolean forceClosing)
-        throws IOException, JsonProcessingException
+        throws IOException
     {
         if (!match.hasMatch()) {
             _reportUnkownFormat(_dataFormatReaders, match);
@@ -1778,10 +1813,11 @@
      * Method called to indicate that format detection failed to detect format
      * of given input
      */
-    protected void _reportUnkownFormat(DataFormatReaders detector, DataFormatReaders.Match match) throws JsonProcessingException
+    protected void _reportUnkownFormat(DataFormatReaders detector, DataFormatReaders.Match match)
+        throws JsonProcessingException
     {
         // 17-Aug-2015, tatu: Unfortunately, no parser/generator available so:
-        throw new JsonParseException(null, "Can not detect format from input, does not look like any of detectable formats "
+        throw new JsonParseException(null, "Cannot detect format from input, does not look like any of detectable formats "
                 +detector.toString());
     }
 
@@ -1798,7 +1834,7 @@
     {
         if (schema != null) {
             if (!_parserFactory.canUseSchema(schema)) {
-                    throw new IllegalArgumentException("Can not use FormatSchema of type "+schema.getClass().getName()
+                    throw new IllegalArgumentException("Cannot use FormatSchema of type "+schema.getClass().getName()
                             +" for format "+_parserFactory.getFormatName());
             }
         }
@@ -1813,13 +1849,6 @@
         return _context.createInstance(_config, p, _injectableValues);
     }
 
-    protected void _reportUndetectableSource(Object src) throws JsonProcessingException
-    {
-        // 17-Aug-2015, tatu: Unfortunately, no parser/generator available so:
-        throw new JsonParseException(null, "Can not use source of type "
-                +src.getClass().getName()+" with format auto-detection: must be byte- not char-based");
-    }
-
     protected InputStream _inputStream(URL src) throws IOException {
         return src.openStream();
     }
@@ -1828,6 +1857,13 @@
         return new FileInputStream(f);
     }
 
+    protected void _reportUndetectableSource(Object src) throws JsonProcessingException
+    {
+        // 17-Aug-2015, tatu: Unfortunately, no parser/generator available so:
+        throw new JsonParseException(null, "Cannot use source of type "
+                +src.getClass().getName()+" with format auto-detection: must be byte- not char-based");
+    }
+
     /*
     /**********************************************************
     /* Helper methods, locating deserializers etc
@@ -1847,9 +1883,9 @@
         // Sanity check: must have actual type...
         JavaType t = _valueType;
         if (t == null) {
-            ctxt.reportMappingException("No value type configured for ObjectReader");
+            ctxt.reportBadDefinition((JavaType) null,
+                    "No value type configured for ObjectReader");
         }
-        
         // First: have we already seen it?
         JsonDeserializer<Object> deser = _rootDeserializers.get(t);
         if (deser != null) {
@@ -1858,7 +1894,7 @@
         // Nope: need to ask provider to resolve it
         deser = ctxt.findRootValueDeserializer(t);
         if (deser == null) { // can this happen?
-            ctxt.reportMappingException("Can not find a deserializer for type %s", t);
+            ctxt.reportBadDefinition(t, "Cannot find a deserializer for type "+t);
         }
         _rootDeserializers.put(t, deser);
         return deser;
@@ -1875,8 +1911,8 @@
             // Nope: need to ask provider to resolve it
             deser = ctxt.findRootValueDeserializer(JSON_NODE_TYPE);
             if (deser == null) { // can this happen?
-                ctxt.reportMappingException("Can not find a deserializer for type %s",
-                        JSON_NODE_TYPE);
+                ctxt.reportBadDefinition(JSON_NODE_TYPE,
+                        "Cannot find a deserializer for type "+JSON_NODE_TYPE);
             }
             _rootDeserializers.put(JSON_NODE_TYPE, deser);
         }
@@ -1890,7 +1926,7 @@
      */
     protected JsonDeserializer<Object> _prefetchRootDeserializer(JavaType valueType)
     {
-        if (valueType == null || !_config.isEnabled(DeserializationFeature.EAGER_DESERIALIZER_FETCH)) {
+        if ((valueType == null) || !_config.isEnabled(DeserializationFeature.EAGER_DESERIALIZER_FETCH)) {
             return null;
         }
         // already cached?
diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectWriter.java b/src/main/java/com/fasterxml/jackson/databind/ObjectWriter.java
index 3a6988b..5098612 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ObjectWriter.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ObjectWriter.java
@@ -2,7 +2,9 @@
 
 import java.io.*;
 import java.text.*;
-import java.util.*;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
 import java.util.concurrent.atomic.AtomicReference;
 
 import com.fasterxml.jackson.core.*;
@@ -222,6 +224,9 @@
      * @since 2.5
      */
     protected ObjectWriter _new(ObjectWriter base, SerializationConfig config) {
+        if (config == _config) {
+            return this;
+        }
         return new ObjectWriter(base, config);
     }
 
@@ -233,6 +238,9 @@
      * @since 2.5
      */
     protected ObjectWriter _new(GeneratorSettings genSettings, Prefetch prefetch) {
+        if ((_generatorSettings == genSettings) && (_prefetch == prefetch)) {
+            return this;
+        }
         return new ObjectWriter(this, _config, genSettings, prefetch);
     }
 
@@ -264,8 +272,7 @@
      * with specified feature enabled.
      */
     public ObjectWriter with(SerializationFeature feature)  {
-        SerializationConfig newConfig = _config.with(feature);
-        return (newConfig == _config) ? this : _new(this, newConfig);
+        return _new(this,  _config.with(feature));
     }
 
     /**
@@ -273,8 +280,7 @@
      * with specified features enabled.
      */
     public ObjectWriter with(SerializationFeature first, SerializationFeature... other) {
-        SerializationConfig newConfig = _config.with(first, other);
-        return (newConfig == _config) ? this : _new(this, newConfig);
+        return _new(this, _config.with(first, other));
     }    
 
     /**
@@ -282,8 +288,7 @@
      * with specified features enabled.
      */
     public ObjectWriter withFeatures(SerializationFeature... features) {
-        SerializationConfig newConfig = _config.withFeatures(features);
-        return (newConfig == _config) ? this : _new(this, newConfig);
+        return _new(this, _config.withFeatures(features));
     }    
     
     /**
@@ -291,8 +296,7 @@
      * with specified feature enabled.
      */
     public ObjectWriter without(SerializationFeature feature) {
-        SerializationConfig newConfig = _config.without(feature);
-        return (newConfig == _config) ? this : _new(this, newConfig);
+        return _new(this, _config.without(feature));
     }    
 
     /**
@@ -300,8 +304,7 @@
      * with specified features enabled.
      */
     public ObjectWriter without(SerializationFeature first, SerializationFeature... other) {
-        SerializationConfig newConfig = _config.without(first, other);
-        return (newConfig == _config) ? this : _new(this, newConfig);
+        return _new(this, _config.without(first, other));
     }    
 
     /**
@@ -309,8 +312,7 @@
      * with specified features enabled.
      */
     public ObjectWriter withoutFeatures(SerializationFeature... features) {
-        SerializationConfig newConfig = _config.withoutFeatures(features);
-        return (newConfig == _config) ? this : _new(this, newConfig);
+        return _new(this, _config.withoutFeatures(features));
     }
 
     /*
@@ -323,32 +325,28 @@
      * @since 2.5
      */
     public ObjectWriter with(JsonGenerator.Feature feature)  {
-        SerializationConfig newConfig = _config.with(feature);
-        return (newConfig == _config) ? this : _new(this, newConfig);
+        return _new(this, _config.with(feature));
     }
 
     /**
      * @since 2.5
      */
     public ObjectWriter withFeatures(JsonGenerator.Feature... features) {
-        SerializationConfig newConfig = _config.withFeatures(features);
-        return (newConfig == _config) ? this : _new(this, newConfig);
+        return _new(this, _config.withFeatures(features));
     }
 
     /**
      * @since 2.5
      */
     public ObjectWriter without(JsonGenerator.Feature feature) {
-        SerializationConfig newConfig = _config.without(feature);
-        return (newConfig == _config) ? this : _new(this, newConfig);
+        return _new(this, _config.without(feature));
     }
 
     /**
      * @since 2.5
      */
     public ObjectWriter withoutFeatures(JsonGenerator.Feature... features) {
-        SerializationConfig newConfig = _config.withoutFeatures(features);
-        return (newConfig == _config) ? this : _new(this, newConfig);
+        return _new(this, _config.withoutFeatures(features));
     }
 
     /*
@@ -361,34 +359,30 @@
      * @since 2.7
      */
     public ObjectWriter with(FormatFeature feature)  {
-        SerializationConfig newConfig = _config.with(feature);
-        return (newConfig == _config) ? this : _new(this, newConfig);
+        return _new(this, _config.with(feature));
     }
 
     /**
      * @since 2.7
      */
     public ObjectWriter withFeatures(FormatFeature... features) {
-        SerializationConfig newConfig = _config.withFeatures(features);
-        return (newConfig == _config) ? this : _new(this, newConfig);
+        return _new(this, _config.withFeatures(features));
     }
 
     /**
      * @since 2.7
      */
     public ObjectWriter without(FormatFeature feature) {
-        SerializationConfig newConfig = _config.without(feature);
-        return (newConfig == _config) ? this : _new(this, newConfig);
+        return _new(this, _config.without(feature));
     }
 
     /**
      * @since 2.7
      */
     public ObjectWriter withoutFeatures(FormatFeature... features) {
-        SerializationConfig newConfig = _config.withoutFeatures(features);
-        return (newConfig == _config) ? this : _new(this, newConfig);
+        return _new(this, _config.withoutFeatures(features));
     }
-    
+
     /*
     /**********************************************************
     /* Life-cycle, fluent factories, type-related
@@ -405,10 +399,8 @@
      * 
      * @since 2.5
      */
-    public ObjectWriter forType(JavaType rootType)
-    {
-        Prefetch pf = _prefetch.forRootType(this, rootType);
-        return (pf == _prefetch) ? this : _new(_generatorSettings, pf);
+    public ObjectWriter forType(JavaType rootType) {
+        return _new(_generatorSettings, _prefetch.forRootType(this, rootType));
     }
 
     /**
@@ -475,8 +467,7 @@
      * rather construct and returns a newly configured instance.
      */
     public ObjectWriter with(DateFormat df) {
-        SerializationConfig newConfig = _config.with(df);
-        return (newConfig == _config) ? this : _new(this, newConfig);
+        return _new(this, _config.with(df));
     }
 
     /**
@@ -492,8 +483,10 @@
      * provider for resolving filter instances by id.
      */
     public ObjectWriter with(FilterProvider filterProvider) {
-        return (filterProvider == _config.getFilterProvider()) ? this
-                 : _new(this, _config.withFilters(filterProvider));
+        if (filterProvider == _config.getFilterProvider()) {
+            return this;
+        }
+        return _new(this, _config.withFilters(filterProvider));
     }
 
     /**
@@ -501,11 +494,7 @@
      * printer (or, if null, will not do any pretty-printing)
      */
     public ObjectWriter with(PrettyPrinter pp) {
-        GeneratorSettings genSet = _generatorSettings.with(pp);
-        if (genSet == _generatorSettings) {
-            return this;
-        }
-        return _new(genSet, _prefetch);
+        return _new(_generatorSettings.with(pp), _prefetch);
     }
 
     /**
@@ -520,16 +509,14 @@
      *    and empty String ("") for "do NOT add root wrapper"
      */
     public ObjectWriter withRootName(String rootName) {
-        SerializationConfig newConfig = _config.withRootName(rootName);
-        return (newConfig == _config) ? this :  _new(this, newConfig);
+        return _new(this, _config.withRootName(rootName));
     }
 
     /**
      * @since 2.6
      */
     public ObjectWriter withRootName(PropertyName rootName) {
-        SerializationConfig newConfig = _config.withRootName(rootName);
-        return (newConfig == _config) ? this :  _new(this, newConfig);
+        return _new(this, _config.withRootName(rootName));
     }
 
     /**
@@ -543,8 +530,7 @@
      * @since 2.6
      */
     public ObjectWriter withoutRootName() {
-        SerializationConfig newConfig = _config.withRootName(PropertyName.NO_NAME);
-        return (newConfig == _config) ? this :  _new(this, newConfig);
+        return _new(this, _config.withRootName(PropertyName.NO_NAME));
     }
     
     /**
@@ -555,12 +541,8 @@
      * rather construct and returns a newly configured instance.
      */
     public ObjectWriter with(FormatSchema schema) {
-        GeneratorSettings genSet = _generatorSettings.with(schema);
-        if (genSet == _generatorSettings) {
-            return this;
-        }
         _verifySchemaType(schema);
-        return _new(genSet, _prefetch);
+        return _new(_generatorSettings.with(schema), _prefetch);
     }
 
     /**
@@ -580,18 +562,15 @@
      * rather construct and returns a newly configured instance.
      */
     public ObjectWriter withView(Class<?> view) {
-        SerializationConfig newConfig = _config.withView(view);
-        return (newConfig == _config) ? this :  _new(this, newConfig);
+        return _new(this, _config.withView(view));
     }    
 
     public ObjectWriter with(Locale l) {
-        SerializationConfig newConfig = _config.with(l);
-        return (newConfig == _config) ? this :  _new(this, newConfig);
+        return _new(this, _config.with(l));
     }
 
     public ObjectWriter with(TimeZone tz) {
-        SerializationConfig newConfig = _config.with(tz);
-        return (newConfig == _config) ? this :  _new(this, newConfig);
+        return _new(this, _config.with(tz));
     }
 
     /**
@@ -601,19 +580,14 @@
      * @since 2.1
      */
     public ObjectWriter with(Base64Variant b64variant) {
-        SerializationConfig newConfig = _config.with(b64variant);
-        return (newConfig == _config) ? this :  _new(this, newConfig);
+        return _new(this, _config.with(b64variant));
     }
 
     /**
      * @since 2.3
      */
     public ObjectWriter with(CharacterEscapes escapes) {
-        GeneratorSettings genSet = _generatorSettings.with(escapes);
-        if (genSet == _generatorSettings) {
-            return this;
-        }
-        return _new(genSet, _prefetch);
+        return _new(_generatorSettings.with(escapes), _prefetch);
     }
 
     /**
@@ -627,8 +601,7 @@
      * @since 2.3
      */
     public ObjectWriter with(ContextAttributes attrs) {
-        SerializationConfig newConfig = _config.with(attrs);
-        return (newConfig == _config) ? this :  _new(this, newConfig);
+        return _new(this, _config.with(attrs));
     }
 
     /**
@@ -638,46 +611,35 @@
      * @since 2.3
      */
     public ObjectWriter withAttributes(Map<?,?> attrs) {
-        SerializationConfig newConfig = _config.withAttributes(attrs);
-        return (newConfig == _config) ? this :  _new(this, newConfig);
+        return _new(this, _config.withAttributes(attrs));
     }
 
     /**
      * @since 2.3
      */
     public ObjectWriter withAttribute(Object key, Object value) {
-        SerializationConfig newConfig = _config.withAttribute(key, value);
-        return (newConfig == _config) ? this :  _new(this, newConfig);
+        return _new(this, _config.withAttribute(key, value));
     }
 
     /**
      * @since 2.3
      */
     public ObjectWriter withoutAttribute(Object key) {
-        SerializationConfig newConfig = _config.withoutAttribute(key);
-        return (newConfig == _config) ? this :  _new(this, newConfig);
+        return _new(this, _config.withoutAttribute(key));
     }
 
     /**
      * @since 2.5
      */
     public ObjectWriter withRootValueSeparator(String sep) {
-        GeneratorSettings genSet = _generatorSettings.withRootValueSeparator(sep);
-        if (genSet == _generatorSettings) {
-            return this;
-        }
-        return _new(genSet, _prefetch);
+        return _new(_generatorSettings.withRootValueSeparator(sep), _prefetch);
     }
 
     /**
      * @since 2.5
      */
     public ObjectWriter withRootValueSeparator(SerializableString sep) {
-        GeneratorSettings genSet = _generatorSettings.withRootValueSeparator(sep);
-        if (genSet == _generatorSettings) {
-            return this;
-        }
-        return _new(genSet, _prefetch);
+        return _new(_generatorSettings.withRootValueSeparator(sep), _prefetch);
     }
 
     /*
@@ -869,7 +831,7 @@
     }
 
     /**
-     * @since 2.8.5
+     * @since 2.9
      */
     @Deprecated
     public boolean isEnabled(JsonParser.Feature f) {
@@ -877,7 +839,7 @@
     }
 
     /**
-     * @since 2.8.5
+     * @since 2.9
      */
     public boolean isEnabled(JsonGenerator.Feature f) {
         return _generatorFactory.isEnabled(f);
@@ -943,7 +905,7 @@
                     gen.flush();
                 }
             } catch (Exception e) {
-                ClassUtil.closeOnFailAndThrowAsIAE(null, toClose, e);
+                ClassUtil.closeOnFailAndThrowAsIOE(null, toClose, e);
                 return;
             }
             toClose.close();
@@ -1029,14 +991,14 @@
         SegmentedStringWriter sw = new SegmentedStringWriter(_generatorFactory._getBufferRecycler());
         try {
             _configAndWriteValue(_generatorFactory.createGenerator(sw), value);
-        } catch (JsonProcessingException e) { // to support [JACKSON-758]
+        } catch (JsonProcessingException e) {
             throw e;
         } catch (IOException e) { // shouldn't really happen, but is declared as possibility so:
             throw JsonMappingException.fromUnexpectedIOE(e);
         }
         return sw.getAndClear();
     }
-    
+
     /**
      * Method that can be used to serialize any Java value as
      * a byte array. Functionally equivalent to calling
@@ -1137,7 +1099,7 @@
     {
         if (schema != null) {
             if (!_generatorFactory.canUseSchema(schema)) {
-                    throw new IllegalArgumentException("Can not use FormatSchema of type "+schema.getClass().getName()
+                    throw new IllegalArgumentException("Cannot use FormatSchema of type "+schema.getClass().getName()
                             +" for format "+_generatorFactory.getFormatName());
             }
         }
@@ -1157,7 +1119,7 @@
         try {
             _prefetch.serialize(gen, value, _serializerProvider());
         } catch (Exception e) {
-            ClassUtil.closeOnFailAndThrowAsIAE(gen, e);
+            ClassUtil.closeOnFailAndThrowAsIOE(gen, e);
             return;
         }
         gen.close();
@@ -1177,7 +1139,7 @@
             toClose = null;
             tmpToClose.close();
         } catch (Exception e) {
-            ClassUtil.closeOnFailAndThrowAsIAE(gen, toClose, e);
+            ClassUtil.closeOnFailAndThrowAsIOE(gen, toClose, e);
             return;
         }
         gen.close();
@@ -1276,11 +1238,13 @@
                 if (rootValueSeparator == null) {
                     return this;
                 }
-            } else if (sep.equals(rootValueSeparator)) {
+                return new GeneratorSettings(prettyPrinter, schema, characterEscapes, null);
+            }
+            if (sep.equals(_rootValueSeparatorAsString())) {
                 return this;
             }
             return new GeneratorSettings(prettyPrinter, schema, characterEscapes,
-                    (sep == null) ? null : new SerializedString(sep));
+                    new SerializedString(sep));
         }
 
         public GeneratorSettings withRootValueSeparator(SerializableString sep) {
@@ -1288,15 +1252,18 @@
                 if (rootValueSeparator == null) {
                     return this;
                 }
-            } else {
-                if (rootValueSeparator != null
-                        && sep.getValue().equals(rootValueSeparator.getValue())) {
-                    return this;
-                }
+                return new GeneratorSettings(prettyPrinter, schema, characterEscapes, null);
+            }
+            if (sep.equals(rootValueSeparator)) {
+                return this;
             }
             return new GeneratorSettings(prettyPrinter, schema, characterEscapes, sep);
         }
 
+        private final String _rootValueSeparatorAsString() {
+            return (rootValueSeparator == null) ? null : rootValueSeparator.getValue();
+        }
+
         /**
          * @since 2.6
          */
@@ -1355,7 +1322,7 @@
         private final JsonSerializer<Object> valueSerializer;
 
         /**
-         * When dealing with polymorphic types, we can not pre-fetch
+         * When dealing with polymorphic types, we cannot pre-fetch
          * serializer, but can pre-fetch {@link TypeSerializer}.
          */
         private final TypeSerializer typeSerializer;
diff --git a/src/main/java/com/fasterxml/jackson/databind/PropertyMetadata.java b/src/main/java/com/fasterxml/jackson/databind/PropertyMetadata.java
index 52ec5b5..9b57186 100644
--- a/src/main/java/com/fasterxml/jackson/databind/PropertyMetadata.java
+++ b/src/main/java/com/fasterxml/jackson/databind/PropertyMetadata.java
@@ -1,9 +1,12 @@
 package com.fasterxml.jackson.databind;
 
+import com.fasterxml.jackson.annotation.Nulls;
+import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
+
 /**
  * Simple container class used for storing "additional" metadata about
  * properties. Carved out to reduce number of distinct properties that
- * actual property implementations and placeholders need to store;
+ * actual property implementations and place holders need to store;
  * since instances are immutable, they can be freely shared.
  * 
  * @since 2.3
@@ -13,12 +16,51 @@
 {
     private static final long serialVersionUID = -1;
 
-    public final static PropertyMetadata STD_REQUIRED = new PropertyMetadata(Boolean.TRUE, null, null, null);
+    public final static PropertyMetadata STD_REQUIRED = new PropertyMetadata(Boolean.TRUE,
+            null, null, null, null, null, null);
 
-    public final static PropertyMetadata STD_OPTIONAL = new PropertyMetadata(Boolean.FALSE, null, null, null);
+    public final static PropertyMetadata STD_OPTIONAL = new PropertyMetadata(Boolean.FALSE,
+            null, null, null, null, null, null);
 
-    public final static PropertyMetadata STD_REQUIRED_OR_OPTIONAL = new PropertyMetadata(null, null, null, null);
-    
+    public final static PropertyMetadata STD_REQUIRED_OR_OPTIONAL = new PropertyMetadata(null,
+            null, null, null, null, null, null);
+
+    /**
+     * Helper class used for containing information about expected merge
+     * information for this property, if merging is expected.
+     *
+     * @since 2.9
+     */
+    public final static class MergeInfo
+    // NOTE: need not be Serializable, not persisted
+    {
+        public final AnnotatedMember getter;
+
+        /**
+         * Flag that is set if the information came from global defaults,
+         * and not from explicit per-property annotations or per-type
+         * config overrides.
+         */
+        public final boolean fromDefaults;
+
+        protected MergeInfo(AnnotatedMember getter, boolean fromDefaults) {
+            this.getter = getter;
+            this.fromDefaults = fromDefaults;
+        }
+
+        public static MergeInfo createForDefaults(AnnotatedMember getter) {
+            return new MergeInfo(getter, true);
+        }
+
+        public static MergeInfo createForTypeOverride(AnnotatedMember getter) {
+            return new MergeInfo(getter, false);
+        }
+
+        public static MergeInfo createForPropertyOverride(AnnotatedMember getter) {
+            return new MergeInfo(getter, false);
+        }
+    }
+
     /**
      * Three states: required, not required and unknown; unknown represented
      * as null.
@@ -38,38 +80,53 @@
     protected final Integer _index;
 
     /**
-     * Optional default value, as String, for property; not used cor
+     * Optional default value, as String, for property; not used for
      * any functionality by core databind, offered as metadata for
      * extensions.
      */
     protected final String _defaultValue;
-    
+
+    /**
+     * Settings regarding merging, if property is determined to possibly
+     * be mergeable (possibly since global settings may be omitted for
+     * non-mergeable types).
+     *<p>
+     * NOTE: transient since it is assumed that this information is only
+     * relevant during initial setup and not needed after full initialization.
+     * May be changed if this proves necessary.
+     * 
+     * @since 2.9
+     */
+    protected final transient MergeInfo _mergeInfo;
+
+    /**
+     * Settings regarding handling of incoming `null`s, both for value itself
+     * and, for structured types, content values (array/Collection elements,
+     * Map values).
+     * 
+     * @since 2.9
+     */
+    protected Nulls _valueNulls, _contentNulls;
+
     /*
     /**********************************************************
     /* Construction, configuration
     /**********************************************************
      */
-    
-    @Deprecated // since 2.4
-    protected PropertyMetadata(Boolean req, String desc) { this(req, desc, null, null); }
 
     /**
-     * @since 2.5
+     * @since 2.9
      */
-    protected PropertyMetadata(Boolean req, String desc, Integer index, String def)
+    protected PropertyMetadata(Boolean req, String desc, Integer index, String def,
+            MergeInfo mergeInfo, Nulls valueNulls, Nulls contentNulls)
     {
         _required = req;
         _description = desc;
         _index = index;
         _defaultValue = (def == null || def.isEmpty()) ? null : def;
-    }
-
-    /**
-     * @since 2.4 Use variant that takes more arguments.
-     */
-    @Deprecated
-    public static PropertyMetadata construct(boolean req, String desc) {
-        return construct(req, desc, null, null);
+        _mergeInfo = mergeInfo;
+        _valueNulls = valueNulls;
+        _contentNulls = contentNulls;
     }
 
     /**
@@ -78,7 +135,8 @@
     public static PropertyMetadata construct(Boolean req, String desc, Integer index,
             String defaultValue) {
         if ((desc != null) || (index != null) || (defaultValue != null)) {
-            return new PropertyMetadata(req, desc, index, defaultValue);
+            return new PropertyMetadata(req, desc, index, defaultValue,
+                    null, null, null);
         }
         if (req == null) {
             return STD_REQUIRED_OR_OPTIONAL;
@@ -90,7 +148,8 @@
     public static PropertyMetadata construct(boolean req, String desc, Integer index,
             String defaultValue) {
         if (desc != null || index != null || defaultValue != null) {
-            return new PropertyMetadata(req, desc, index, defaultValue);
+            return new PropertyMetadata(req, desc, index, defaultValue,
+                    null, null, null);
         }
         return req ? STD_REQUIRED : STD_OPTIONAL;
     }
@@ -101,7 +160,9 @@
      */
     protected Object readResolve()
     {
-        if (_description == null && _index == null && _defaultValue == null) {
+        if ((_description == null) && (_index == null) && (_defaultValue == null)
+                && (_mergeInfo == null)
+                && (_valueNulls == null) && (_contentNulls == null)) {
             if (_required == null) {
                 return STD_REQUIRED_OR_OPTIONAL;
             }
@@ -111,7 +172,25 @@
     }
 
     public PropertyMetadata withDescription(String desc) {
-        return new PropertyMetadata(_required, desc, _index, _defaultValue);
+        return new PropertyMetadata(_required, desc, _index, _defaultValue,
+                _mergeInfo, _valueNulls, _contentNulls);
+    }
+
+    /**
+     * @since 2.9
+     */
+    public PropertyMetadata withMergeInfo(MergeInfo mergeInfo) {
+        return new PropertyMetadata(_required, _description, _index, _defaultValue,
+                mergeInfo, _valueNulls, _contentNulls);
+    }
+
+    /**
+     * @since 2.9
+     */
+    public PropertyMetadata withNulls(Nulls valueNulls,
+            Nulls contentNulls) {
+        return new PropertyMetadata(_required, _description, _index, _defaultValue,
+                _mergeInfo, valueNulls, contentNulls);
     }
 
     public PropertyMetadata withDefaultValue(String def) {
@@ -120,14 +199,16 @@
                 return this;
             }
             def = null;
-        } else if (_defaultValue.equals(def)) {
+        } else if (def.equals(_defaultValue)) {
             return this;
         }
-        return new PropertyMetadata(_required, _description, _index, def);
+        return new PropertyMetadata(_required, _description, _index, def,
+                _mergeInfo, _valueNulls, _contentNulls);
     }
     
     public PropertyMetadata withIndex(Integer index) {
-        return new PropertyMetadata(_required, _description, index, _defaultValue);
+        return new PropertyMetadata(_required, _description, index, _defaultValue,
+                _mergeInfo, _valueNulls, _contentNulls);
     }
     
     public PropertyMetadata withRequired(Boolean b) {
@@ -135,14 +216,13 @@
             if (_required == null) {
                 return this;
             }
-        } else {
-            if (_required != null && _required.booleanValue() == b.booleanValue()) {
-                return this;
-            }
+        } else if (b.equals(_required)) {
+            return this;
         }
-        return new PropertyMetadata(b, _description, _index, _defaultValue);
+        return new PropertyMetadata(b, _description, _index, _defaultValue,
+                _mergeInfo, _valueNulls, _contentNulls);
     }
-    
+
     /*
     /**********************************************************
     /* Accessors
@@ -157,21 +237,15 @@
     public String getDefaultValue() { return _defaultValue; }
 
     /**
-     * @deprecated Since 2.6: typo in name, use {@link #hasDefaultValue()} instead.
-     */
-    @Deprecated
-    public boolean hasDefuaultValue() { return hasDefaultValue(); }
-
-    /**
      * Accessor for determining whether property has declared "default value",
      * which may be used by extension modules.
      *
      * @since 2.6
      */
     public boolean hasDefaultValue() { return (_defaultValue != null); }
-    
+
     public boolean isRequired() { return (_required != null) && _required.booleanValue(); }
-    
+
     public Boolean getRequired() { return _required; }
 
     /**
@@ -183,4 +257,19 @@
      * @since 2.4
      */
     public boolean hasIndex() { return _index != null; }
+
+    /**
+     * @since 2.9
+     */
+    public MergeInfo getMergeInfo() { return _mergeInfo; }
+
+    /**
+     * @since 2.9
+     */
+    public Nulls getValueNulls() { return _valueNulls; }
+
+    /**
+     * @since 2.9
+     */
+    public Nulls getContentNulls() { return _contentNulls; }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/PropertyName.java b/src/main/java/com/fasterxml/jackson/databind/PropertyName.java
index 10c24b1..5fcd044 100644
--- a/src/main/java/com/fasterxml/jackson/databind/PropertyName.java
+++ b/src/main/java/com/fasterxml/jackson/databind/PropertyName.java
@@ -4,6 +4,7 @@
 import com.fasterxml.jackson.core.io.SerializedString;
 import com.fasterxml.jackson.core.util.InternCache;
 import com.fasterxml.jackson.databind.cfg.MapperConfig;
+import com.fasterxml.jackson.databind.util.ClassUtil;
 
 /**
  * Simple value class used for containing names of properties as defined
@@ -62,17 +63,23 @@
 
     public PropertyName(String simpleName, String namespace)
     {
-        _simpleName = (simpleName == null) ? "" : simpleName;
+        _simpleName = ClassUtil.nonNullString(simpleName);
         _namespace = namespace;
     }
 
     // To support JDK serialization, recovery of Singleton instance
     protected Object readResolve() {
-        if (_simpleName == null || _USE_DEFAULT.equals(_simpleName)) {
-            return USE_DEFAULT;
-        }
-        if (_simpleName.equals(_NO_NAME) && _namespace == null) {
-            return NO_NAME;
+        if (_namespace == null) {
+            if (_simpleName == null || _USE_DEFAULT.equals(_simpleName)) {
+                return USE_DEFAULT;
+            }
+            // 30-Oct-2016, tatu: I don't see how this could ever occur...
+            //     or how to distinguish USE_DEFAULT/NO_NAME from serialized
+            /*
+            if (_simpleName.equals(_NO_NAME)) {
+                return NO_NAME;
+            }
+            */
         }
         return this;
     }
@@ -182,10 +189,8 @@
      * @since 2.3
      */
     public boolean hasSimpleName(String str) {
-        if (str == null) {
-            return _simpleName == null;
-        }
-        return str.equals(_simpleName);
+        // _simpleName never null so...
+        return _simpleName.equals(str);
     }
     
     public boolean hasNamespace() {
diff --git a/src/main/java/com/fasterxml/jackson/databind/SequenceWriter.java b/src/main/java/com/fasterxml/jackson/databind/SequenceWriter.java
index c6d8bc3..3902cf6 100644
--- a/src/main/java/com/fasterxml/jackson/databind/SequenceWriter.java
+++ b/src/main/java/com/fasterxml/jackson/databind/SequenceWriter.java
@@ -172,7 +172,7 @@
         if (_cfgCloseCloseable && (value instanceof Closeable)) {
             return _writeCloseableValue(value, type);
         }
-        /* 15-Dec-2014, tatu: I wonder if this could be come problematic. It shouldn't
+        /* 15-Dec-2014, tatu: I wonder if this could become problematic. It shouldn't
          *   really, since trying to use differently paramterized types in a sequence
          *   is likely to run into other issues. But who knows; if it does become an
          *   issue, may need to implement alternative, JavaType-based map.
@@ -196,7 +196,7 @@
         return this;
     }
 
-    // NOTE: redundant wrt variant that takes Iterable, but can not remove or even
+    // NOTE: redundant wrt variant that takes Iterable, but cannot remove or even
     // deprecate due to backwards-compatibility needs
     public <C extends Collection<?>> SequenceWriter writeAll(C container) throws IOException {
         for (Object value : container) {
diff --git a/src/main/java/com/fasterxml/jackson/databind/SerializationConfig.java b/src/main/java/com/fasterxml/jackson/databind/SerializationConfig.java
index f57590f..eece8c6 100644
--- a/src/main/java/com/fasterxml/jackson/databind/SerializationConfig.java
+++ b/src/main/java/com/fasterxml/jackson/databind/SerializationConfig.java
@@ -1,7 +1,6 @@
 package com.fasterxml.jackson.databind;
 
 import java.text.DateFormat;
-import java.util.*;
 
 import com.fasterxml.jackson.annotation.*;
 
@@ -10,14 +9,10 @@
 import com.fasterxml.jackson.core.util.Instantiatable;
 
 import com.fasterxml.jackson.databind.cfg.*;
-import com.fasterxml.jackson.databind.introspect.ClassIntrospector;
 import com.fasterxml.jackson.databind.introspect.SimpleMixInResolver;
-import com.fasterxml.jackson.databind.introspect.VisibilityChecker;
 import com.fasterxml.jackson.databind.jsontype.SubtypeResolver;
-import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
 import com.fasterxml.jackson.databind.ser.FilterProvider;
 import com.fasterxml.jackson.databind.ser.SerializerFactory;
-import com.fasterxml.jackson.databind.type.TypeFactory;
 import com.fasterxml.jackson.databind.util.RootNameLookup;
 
 /**
@@ -41,10 +36,6 @@
     // since 2.6
     protected final static PrettyPrinter DEFAULT_PRETTY_PRINTER = new DefaultPrettyPrinter();
 
-    // since 2.7
-    // Default is "USE_DEFAULTS, USE_DEFAULTS"
-    protected final static JsonInclude.Value DEFAULT_INCLUSION = JsonInclude.Value.empty();
-
     /*
     /**********************************************************
     /* Configured helper objects
@@ -104,36 +95,21 @@
      * @since 2.7
      */
     protected final int _formatWriteFeaturesToChange;
-    
-    /*
-    /**********************************************************
-    /* Other configuration
-    /**********************************************************
-     */
-    
-    /**
-     * Which Bean/Map properties are to be included in serialization?
-     * Default settings is to include all regardless of value; can be
-     * changed to only include non-null properties, or properties
-     * with non-default values.
-     *<p>
-     * NOTE: type changed in 2.7, to include both value and content
-     * inclusion options./
-     */
-    protected final JsonInclude.Value _serializationInclusion;
 
     /*
     /**********************************************************
-    /* Life-cycle, constructors
+    /* Life-cycle, primary constructors for new instances
     /**********************************************************
      */
 
     /**
      * Constructor used by ObjectMapper to create default configuration object instance.
+     *
+     * @since 2.9
      */
     public SerializationConfig(BaseSettings base,
-            SubtypeResolver str, SimpleMixInResolver mixins,
-            RootNameLookup rootNames, ConfigOverrides configOverrides)
+            SubtypeResolver str, SimpleMixInResolver mixins, RootNameLookup rootNames,
+            ConfigOverrides configOverrides)
     {
         super(base, str, mixins, rootNames, configOverrides);
         _serFeatures = collectFeatureDefaults(SerializationFeature.class);
@@ -143,25 +119,38 @@
         _generatorFeaturesToChange = 0;
         _formatWriteFeatures = 0;
         _formatWriteFeaturesToChange = 0;
-        _serializationInclusion = DEFAULT_INCLUSION;
     }
 
     /**
-     * @deprecated Since 2.8, remove from 2.9 or later
+     * Copy-constructor used for making a copy to be used by new {@link ObjectMapper}.
+     *
+     * @since 2.9
      */
-    @Deprecated
-    public SerializationConfig(BaseSettings base,
-            SubtypeResolver str, SimpleMixInResolver mixins,
-            RootNameLookup rootNames)
+    protected SerializationConfig(SerializationConfig src,
+            SimpleMixInResolver mixins, RootNameLookup rootNames,
+            ConfigOverrides configOverrides)
     {
-        this(base, str, mixins, rootNames, null);
+        super(src, mixins, rootNames, configOverrides);
+        _serFeatures = src._serFeatures;
+        _filterProvider = src._filterProvider;
+        _defaultPrettyPrinter = src._defaultPrettyPrinter;
+        _generatorFeatures = src._generatorFeatures;
+        _generatorFeaturesToChange = src._generatorFeaturesToChange;
+        _formatWriteFeatures = src._formatWriteFeatures;
+        _formatWriteFeaturesToChange = src._formatWriteFeaturesToChange;
     }
-    
+
+    /*
+    /**********************************************************
+    /* Life-cycle, secondary constructors to support
+    /* "mutant factories", with single property changes
+    /**********************************************************
+     */
+
     private SerializationConfig(SerializationConfig src, SubtypeResolver str)
     {
         super(src, str);
         _serFeatures = src._serFeatures;
-        _serializationInclusion = src._serializationInclusion;
         _filterProvider = src._filterProvider;
         _defaultPrettyPrinter = src._defaultPrettyPrinter;
         _generatorFeatures = src._generatorFeatures;
@@ -177,7 +166,6 @@
     {
         super(src, mapperFeatures);
         _serFeatures = serFeatures;
-        _serializationInclusion = src._serializationInclusion;
         _filterProvider = src._filterProvider;
         _defaultPrettyPrinter = src._defaultPrettyPrinter;
         _generatorFeatures = generatorFeatures;
@@ -190,7 +178,6 @@
     {
         super(src, base);
         _serFeatures = src._serFeatures;
-        _serializationInclusion = src._serializationInclusion;
         _filterProvider = src._filterProvider;
         _defaultPrettyPrinter = src._defaultPrettyPrinter;
         _generatorFeatures = src._generatorFeatures;
@@ -203,7 +190,6 @@
     {
         super(src);
         _serFeatures = src._serFeatures;
-        _serializationInclusion = src._serializationInclusion;
         _filterProvider = filters;
         _defaultPrettyPrinter = src._defaultPrettyPrinter;
         _generatorFeatures = src._generatorFeatures;
@@ -216,20 +202,6 @@
     {
         super(src, view);
         _serFeatures = src._serFeatures;
-        _serializationInclusion = src._serializationInclusion;
-        _filterProvider = src._filterProvider;
-        _defaultPrettyPrinter = src._defaultPrettyPrinter;
-        _generatorFeatures = src._generatorFeatures;
-        _generatorFeaturesToChange = src._generatorFeaturesToChange;
-        _formatWriteFeatures = src._formatWriteFeatures;
-        _formatWriteFeaturesToChange = src._formatWriteFeaturesToChange;
-    }
-
-    private SerializationConfig(SerializationConfig src, JsonInclude.Value incl)
-    {
-        super(src);
-        _serFeatures = src._serFeatures;
-        _serializationInclusion = incl;
         _filterProvider = src._filterProvider;
         _defaultPrettyPrinter = src._defaultPrettyPrinter;
         _generatorFeatures = src._generatorFeatures;
@@ -242,7 +214,6 @@
     {
         super(src, rootName);
         _serFeatures = src._serFeatures;
-        _serializationInclusion = src._serializationInclusion;
         _filterProvider = src._filterProvider;
         _defaultPrettyPrinter = src._defaultPrettyPrinter;
         _generatorFeatures = src._generatorFeatures;
@@ -258,7 +229,6 @@
     {
         super(src, attrs);
         _serFeatures = src._serFeatures;
-        _serializationInclusion = src._serializationInclusion;
         _filterProvider = src._filterProvider;
         _defaultPrettyPrinter = src._defaultPrettyPrinter;
         _generatorFeatures = src._generatorFeatures;
@@ -274,7 +244,6 @@
     {
         super(src, mixins);
         _serFeatures = src._serFeatures;
-        _serializationInclusion = src._serializationInclusion;
         _filterProvider = src._filterProvider;
         _defaultPrettyPrinter = src._defaultPrettyPrinter;
         _generatorFeatures = src._generatorFeatures;
@@ -290,7 +259,6 @@
     {
         super(src);
         _serFeatures = src._serFeatures;
-        _serializationInclusion = src._serializationInclusion;
         _filterProvider = src._filterProvider;
         _defaultPrettyPrinter = defaultPP;
         _generatorFeatures = src._generatorFeatures;
@@ -299,126 +267,23 @@
         _formatWriteFeaturesToChange = src._formatWriteFeaturesToChange;
     }
 
-    /**
-     * Copy-constructor used for making a copy to be used by new {@link ObjectMapper}.
-     *
-     * @since 2.8
-     */
-    protected SerializationConfig(SerializationConfig src, SimpleMixInResolver mixins,
-            RootNameLookup rootNames, ConfigOverrides configOverrides)
-    {
-        super(src, mixins, rootNames, configOverrides);
-        _serFeatures = src._serFeatures;
-        _serializationInclusion = src._serializationInclusion;
-        _filterProvider = src._filterProvider;
-        _defaultPrettyPrinter = src._defaultPrettyPrinter;
-        _generatorFeatures = src._generatorFeatures;
-        _generatorFeaturesToChange = src._generatorFeaturesToChange;
-        _formatWriteFeatures = src._formatWriteFeatures;
-        _formatWriteFeaturesToChange = src._formatWriteFeaturesToChange;
-    }
-
     /*
     /**********************************************************
-    /* Life-cycle, factory methods from MapperConfig
+    /* Life-cycle, factory methods from MapperConfig(Base)
     /**********************************************************
      */
 
-    /**
-     * Fluent factory method that will construct and return a new configuration
-     * object instance with specified features enabled.
-     */
-    @Override
-    public SerializationConfig with(MapperFeature... features)
-    {
-        int newMapperFlags = _mapperFeatures;
-        for (MapperFeature f : features) {
-            newMapperFlags |= f.getMask();
-        }
-        return (newMapperFlags == _mapperFeatures) ? this
-                : new SerializationConfig(this, newMapperFlags, _serFeatures,
+    @Override // since 2.9
+    protected final SerializationConfig _withBase(BaseSettings newBase) {
+        return (_base == newBase) ? this : new SerializationConfig(this, newBase);
+    }
+
+    @Override // since 2.9
+    protected final SerializationConfig _withMapperFeatures(int mapperFeatures) {
+        return new SerializationConfig(this, mapperFeatures, _serFeatures,
                         _generatorFeatures, _generatorFeaturesToChange,
                         _formatWriteFeatures, _formatWriteFeaturesToChange);
     }
-    
-    /**
-     * Fluent factory method that will construct and return a new configuration
-     * object instance with specified features disabled.
-     */
-    @Override
-    public SerializationConfig without(MapperFeature... features)
-    {
-        int newMapperFlags = _mapperFeatures;
-        for (MapperFeature f : features) {
-             newMapperFlags &= ~f.getMask();
-        }
-        return (newMapperFlags == _mapperFeatures) ? this
-                : new SerializationConfig(this, newMapperFlags, _serFeatures,
-                        _generatorFeatures, _generatorFeaturesToChange,
-                        _formatWriteFeatures, _formatWriteFeaturesToChange);
-    }
-
-    @Override
-    public SerializationConfig with(MapperFeature feature, boolean state)
-    {
-        int newMapperFlags;
-        if (state) {
-            newMapperFlags = _mapperFeatures | feature.getMask();
-        } else {
-            newMapperFlags = _mapperFeatures & ~feature.getMask();
-        }
-        return (newMapperFlags == _mapperFeatures) ? this
-            : new SerializationConfig(this, newMapperFlags, _serFeatures,
-                    _generatorFeatures, _generatorFeaturesToChange,
-                    _formatWriteFeatures, _formatWriteFeaturesToChange);
-    }
-    
-    @Override
-    public SerializationConfig with(AnnotationIntrospector ai) {
-        return _withBase(_base.withAnnotationIntrospector(ai));
-    }
-
-    @Override
-    public SerializationConfig withAppendedAnnotationIntrospector(AnnotationIntrospector ai) {
-        return _withBase(_base.withAppendedAnnotationIntrospector(ai));
-    }
-
-    @Override
-    public SerializationConfig withInsertedAnnotationIntrospector(AnnotationIntrospector ai) {
-        return _withBase(_base.withInsertedAnnotationIntrospector(ai));
-    }
-
-    @Override
-    public SerializationConfig with(ClassIntrospector ci) {
-        return _withBase(_base.withClassIntrospector(ci));
-    }
-
-    /**
-     * In addition to constructing instance with specified date format,
-     * will enable or disable <code>SerializationFeature.WRITE_DATES_AS_TIMESTAMPS</code>
-     * (enable if format set as null; disable if non-null)
-     */
-    @Override
-    public SerializationConfig with(DateFormat df) {
-        SerializationConfig cfg =  new SerializationConfig(this, _base.withDateFormat(df));
-        // Also need to toggle this feature based on existence of date format:
-        if (df == null) {
-            cfg = cfg.with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
-        } else {
-            cfg = cfg.without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
-        }
-        return cfg;
-    }
-
-    @Override
-    public SerializationConfig with(HandlerInstantiator hi) {
-        return _withBase(_base.withHandlerInstantiator(hi));
-    }
-
-    @Override
-    public SerializationConfig with(PropertyNamingStrategy pns) {
-        return _withBase(_base.withPropertyNamingStrategy(pns));
-    }
 
     @Override
     public SerializationConfig withRootName(PropertyName rootName) {
@@ -438,52 +303,34 @@
     }
 
     @Override
-    public SerializationConfig with(TypeFactory tf) {
-        return _withBase(_base.withTypeFactory(tf));
-    }
-
-    @Override
-    public SerializationConfig with(TypeResolverBuilder<?> trb) {
-        return _withBase(_base.withTypeResolverBuilder(trb));
-    }
-
-    @Override
     public SerializationConfig withView(Class<?> view) {
         return (_view == view) ? this : new SerializationConfig(this, view);
     }
-
-    @Override
-    public SerializationConfig with(VisibilityChecker<?> vc) {
-        return _withBase(_base.withVisibilityChecker(vc));
-    }
-
-    @Override
-    public SerializationConfig withVisibility(PropertyAccessor forMethod, JsonAutoDetect.Visibility visibility) {
-        return _withBase(_base.withVisibility(forMethod, visibility));
-    }
-
-    @Override
-    public SerializationConfig with(Locale l) {
-        return _withBase(_base.with(l));
-    }
-
-    @Override
-    public SerializationConfig with(TimeZone tz) {
-        return _withBase(_base.with(tz));
-    }
-
-    @Override
-    public SerializationConfig with(Base64Variant base64) {
-        return _withBase(_base.with(base64));
-    }
-
+    
     @Override
     public SerializationConfig with(ContextAttributes attrs) {
         return (attrs == _attributes) ? this : new SerializationConfig(this, attrs);
     }
 
-    private final SerializationConfig _withBase(BaseSettings newBase) {
-        return (_base == newBase) ? this : new SerializationConfig(this, newBase);
+    /*
+    /**********************************************************
+    /* Factory method overrides
+    /**********************************************************
+     */
+
+    /**
+     * In addition to constructing instance with specified date format,
+     * will enable or disable <code>SerializationFeature.WRITE_DATES_AS_TIMESTAMPS</code>
+     * (enable if format set as null; disable if non-null)
+     */
+    @Override
+    public SerializationConfig with(DateFormat df) {
+        SerializationConfig cfg = super.with(df);
+        // Also need to toggle this feature based on existence of date format:
+        if (df == null) {
+            return cfg.with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
+        }
+        return cfg.without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
     }
 
     /*
@@ -751,24 +598,20 @@
     }
 
     /**
-     * @deprecated Since 2.7 use {@link #withPropertyInclusion} instead
+     * Mutant factory method for constructing a new instance with different
+     * default inclusion criteria configuration.
+     *
+     * @since 2.7
+     *
+     * @deprecated Since 2.9; not needed any more
      */
     @Deprecated
-    public SerializationConfig withSerializationInclusion(JsonInclude.Include incl) {
-        return withPropertyInclusion(DEFAULT_INCLUSION.withValueInclusion(incl));
+    public SerializationConfig withPropertyInclusion(JsonInclude.Value incl) {
+        _configOverrides.setDefaultInclusion(incl);
+        return this;
     }
 
     /**
-     * @since 2.7
-     */
-    public SerializationConfig withPropertyInclusion(JsonInclude.Value incl) {
-        if (_serializationInclusion.equals(incl)) {
-            return this;
-        }
-        return new SerializationConfig(this, incl);
-    }
-    
-    /**
      * @since 2.6
      */
     public SerializationConfig withDefaultPrettyPrinter(PrettyPrinter pp) {
@@ -834,85 +677,20 @@
 
     /*
     /**********************************************************
-    /* MapperConfig implementation/overrides: introspection
-    /**********************************************************
-     */
-
-    @Override
-    public AnnotationIntrospector getAnnotationIntrospector()
-    {
-        if (isEnabled(MapperFeature.USE_ANNOTATIONS)) {
-            return super.getAnnotationIntrospector();
-        }
-        return AnnotationIntrospector.nopInstance();
-    }
-
-    /**
-     * Accessor for getting bean description that only contains class
-     * annotations: useful if no getter/setter/creator information is needed.
-     */
-    @Override
-    public BeanDescription introspectClassAnnotations(JavaType type) {
-        return getClassIntrospector().forClassAnnotations(this, type, this);
-    }
-
-    /**
-     * Accessor for getting bean description that only contains immediate class
-     * annotations: ones from the class, and its direct mix-in, if any, but
-     * not from super types.
-     */
-    @Override
-    public BeanDescription introspectDirectClassAnnotations(JavaType type) {
-        return getClassIntrospector().forDirectClassAnnotations(this, type, this);
-    }
-
-    /*
-    /**********************************************************
     /* Configuration: default settings with per-type overrides
     /**********************************************************
      */
-    
+
     /**
      * @deprecated Since 2.7 use {@link #getDefaultPropertyInclusion} instead
      */
     @Deprecated
     public JsonInclude.Include getSerializationInclusion()
     {
-        JsonInclude.Include incl = _serializationInclusion.getValueInclusion();
+        JsonInclude.Include incl = getDefaultPropertyInclusion().getValueInclusion();
         return (incl == JsonInclude.Include.USE_DEFAULTS) ? JsonInclude.Include.ALWAYS : incl;
     }
 
-    @Override
-    public JsonInclude.Value getDefaultPropertyInclusion() {
-        return _serializationInclusion;
-    }
-
-    @Override
-    public JsonInclude.Value getDefaultPropertyInclusion(Class<?> baseType) {
-        ConfigOverride overrides = findConfigOverride(baseType);
-        if (overrides != null) {
-            JsonInclude.Value v = overrides.getInclude();
-            if (v != null) {
-                return v;
-            }
-        }
-        return _serializationInclusion;
-    }
-
-    @Override
-    public JsonInclude.Value getDefaultPropertyInclusion(Class<?> baseType,
-            JsonInclude.Value defaultIncl)
-    {
-        ConfigOverride overrides = findConfigOverride(baseType);
-        if (overrides != null) {
-            JsonInclude.Value v = overrides.getInclude();
-            if (v != null) {
-                return v;
-            }
-        }
-        return defaultIncl;
-    }
-
     /*
     /**********************************************************
     /* Configuration: other
@@ -970,7 +748,7 @@
     public FilterProvider getFilterProvider() {
         return _filterProvider;
     }
-
+    
     /**
      * Accessor for configured blueprint "default" {@link PrettyPrinter} to
      * use, if default pretty-printing is enabled.
@@ -999,15 +777,4 @@
     public <T extends BeanDescription> T introspect(JavaType type) {
         return (T) getClassIntrospector().forSerialization(this, type, this);
     }
-    
-    /*
-    /**********************************************************
-    /* Debug support
-    /**********************************************************
-     */
-
-    @Override
-    public String toString() {
-        return "[SerializationConfig: flags=0x"+Integer.toHexString(_serFeatures)+"]";
-    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/SerializationFeature.java b/src/main/java/com/fasterxml/jackson/databind/SerializationFeature.java
index e8d1cb3..1b46dc0 100644
--- a/src/main/java/com/fasterxml/jackson/databind/SerializationFeature.java
+++ b/src/main/java/com/fasterxml/jackson/databind/SerializationFeature.java
@@ -275,8 +275,17 @@
      * Feature that determines whether Map entries with null values are
      * to be serialized (true) or not (false).
      *<p>
+     * NOTE: unlike other {@link SerializationFeature}s, this feature <b>cannot</b> be
+     * dynamically changed on per-call basis, because its effect is considered during
+     * construction of serializers and property handlers.
+     *<p>
      * Feature is enabled by default.
+     *
+     * @deprecated Since 2.9 there are better mechanism for specifying filtering; specifically
+     *   using {@link com.fasterxml.jackson.annotation.JsonInclude} or configuration overrides
+     *   (see {@link ObjectMapper#configOverride(Class)}}).
      */
+    @Deprecated // since 2.9
     WRITE_NULL_MAP_VALUES(true),
 
     /**
@@ -288,7 +297,7 @@
      * Note that this does not change behavior of {@link java.util.Map}s, or
      * "Collection-like" types.
      *<p>
-     * NOTE: unlike other {@link SerializationFeature}s, this feature <b>can not</b> be
+     * NOTE: unlike other {@link SerializationFeature}s, this feature <b>cannot</b> be
      * dynamically changed on per-call basis, because its effect is considered during
      * construction of serializers and property handlers.
      *<p>
diff --git a/src/main/java/com/fasterxml/jackson/databind/SerializerProvider.java b/src/main/java/com/fasterxml/jackson/databind/SerializerProvider.java
index bce67fa..11a4109 100644
--- a/src/main/java/com/fasterxml/jackson/databind/SerializerProvider.java
+++ b/src/main/java/com/fasterxml/jackson/databind/SerializerProvider.java
@@ -12,6 +12,8 @@
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.databind.cfg.ContextAttributes;
 import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
+import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
+import com.fasterxml.jackson.databind.exc.InvalidTypeIdException;
 import com.fasterxml.jackson.databind.introspect.Annotated;
 import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
 import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
@@ -211,9 +213,6 @@
     protected SerializerProvider(SerializerProvider src,
             SerializationConfig config, SerializerFactory f)
     {
-        if (config == null) {
-            throw new NullPointerException();
-        }
         _serializerFactory = f;
         _config = config;
 
@@ -273,7 +272,7 @@
     public void setDefaultKeySerializer(JsonSerializer<Object> ks)
     {
         if (ks == null) {
-            throw new IllegalArgumentException("Can not pass null JsonSerializer");
+            throw new IllegalArgumentException("Cannot pass null JsonSerializer");
         }
         _keySerializer = ks;
     }
@@ -290,7 +289,7 @@
     public void setNullValueSerializer(JsonSerializer<Object> nvs)
     {
         if (nvs == null) {
-            throw new IllegalArgumentException("Can not pass null JsonSerializer");
+            throw new IllegalArgumentException("Cannot pass null JsonSerializer");
         }
         _nullValueSerializer = nvs;
     }
@@ -307,7 +306,7 @@
     public void setNullKeySerializer(JsonSerializer<Object> nks)
     {
         if (nks == null) {
-            throw new IllegalArgumentException("Can not pass null JsonSerializer");
+            throw new IllegalArgumentException("Cannot pass null JsonSerializer");
         }
         _nullKeySerializer = nks;
     }
@@ -445,6 +444,17 @@
         return _config.getFilterProvider();
     }
 
+    /**
+     *<p>
+     * NOTE: current implementation simply returns `null` as generator is not yet
+     * assigned to this provider.
+     *
+     * @since 2.8
+     */
+    public JsonGenerator getGenerator() {
+        return null;
+    }
+    
     /*
     /**********************************************************
     /* Access to Object Id aspects
@@ -519,6 +529,8 @@
      * full generics-aware type instead of raw class.
      * This is necessary for accurate handling of external type information,
      * to handle polymorphic types.
+     *<p>
+     * Note: this call will also contextualize serializer before returning it.
      * 
      * @param property When creating secondary serializers, property for which
      *   serializer is needed: annotations of the property (or bean that contains it)
@@ -528,6 +540,9 @@
     public JsonSerializer<Object> findValueSerializer(JavaType valueType, BeanProperty property)
         throws JsonMappingException
     {
+        if (valueType == null) {
+            reportMappingProblem("Null passed for `valueType` of `findValueSerializer()`");
+        }
         // (see comments from above method)
         JsonSerializer<Object> ser = _knownSerializers.untypedValueSerializer(valueType);
         if (ser == null) {
@@ -822,7 +837,7 @@
     /**
      * Method called to find a serializer to use for null values for given
      * declared type. Note that type is completely based on declared type,
-     * since nulls in Java have no type and thus runtime type can not be
+     * since nulls in Java have no type and thus runtime type cannot be
      * determined.
      * 
      * @since 2.0
@@ -852,7 +867,7 @@
 
     /**
      * Method called to get the serializer to use if provider
-     * can not determine an actual type-specific serializer
+     * cannot determine an actual type-specific serializer
      * to use; typically when none of {@link SerializerFactory}
      * instances are able to construct a serializer.
      *<p>
@@ -913,6 +928,29 @@
             Object serDef)
         throws JsonMappingException;
 
+    /**
+     * Method that can be called to construct and configure {@link JsonInclude}
+     * filter instance,
+     * given a {@link Class} to instantiate (with default constructor, by default).
+     *
+     * @param forProperty (optional) If filter is created for a property, that property;
+     *    `null` if filter created via defaulting, global or per-type.
+     *
+     * @since 2.9
+     */
+    public abstract Object includeFilterInstance(BeanPropertyDefinition forProperty,
+            Class<?> filterClass)
+        throws JsonMappingException;
+
+    /**
+     * Follow-up method that may be called after calling {@link #includeFilterInstance},
+     * to check handling of `null` values by the filter.
+     *
+     * @since 2.9
+     */
+    public abstract boolean includeFilterSuppressNulls(Object filter)
+        throws JsonMappingException;
+
     /*
     /**********************************************************
     /* Support for contextualization
@@ -1097,34 +1135,6 @@
      */
 
     /**
-     * Factory method for constructing a {@link JsonMappingException};
-     * usually only indirectly used by calling
-     * {@link #reportMappingProblem(String, Object...)}.
-     *
-     * @since 2.6
-     */
-    public JsonMappingException mappingException(String message, Object... args) {
-        if (args != null && args.length > 0) {
-            message = String.format(message, args);
-        }
-        return JsonMappingException.from(getGenerator(), message);
-    }
-
-    /**
-     * Factory method for constructing a {@link JsonMappingException};
-     * usually only indirectly used by calling
-     * {@link #reportMappingProblem(Throwable, String, Object...)}
-     * 
-     * @since 2.8
-     */
-    protected JsonMappingException mappingException(Throwable t, String message, Object... args) {
-        if (args != null && args.length > 0) {
-            message = String.format(message, args);
-        }
-        return JsonMappingException.from(getGenerator(), message, t);
-    }
-
-    /**
      * Helper method called to indicate problem; default behavior is to construct and
      * throw a {@link JsonMappingException}, but in future may collect more than one
      * and only throw after certain number, or at the end of serialization.
@@ -1136,17 +1146,6 @@
     }
 
     /**
-     * Helper method called to indicate problem; default behavior is to construct and
-     * throw a {@link JsonMappingException}, but in future may collect more than one
-     * and only throw after certain number, or at the end of serialization.
-     *
-     * @since 2.8
-     */
-    public void reportMappingProblem(Throwable t, String message, Object... args) throws JsonMappingException {
-        throw mappingException(t, message, args);
-    }
-
-    /**
      * Helper method called to indicate problem in POJO (serialization) definitions or settings
      * regarding specific Java type, unrelated to actual JSON content to map.
      * Default behavior is to construct and throw a {@link JsonMappingException}.
@@ -1154,13 +1153,14 @@
      * @since 2.9
      */
     public <T> T reportBadTypeDefinition(BeanDescription bean,
-            String message, Object... args) throws JsonMappingException {
-        if (args != null && args.length > 0) {
-            message = String.format(message, args);
+            String msg, Object... msgArgs) throws JsonMappingException {
+        String beanDesc = "N/A";
+        if (bean != null) {
+            beanDesc = ClassUtil.nameOf(bean.getBeanClass());
         }
-        String beanDesc = (bean == null) ? "N/A" : _desc(bean.getType().getGenericSignature());
-        throw mappingException("Invalid type definition for type %s: %s",
-                beanDesc, message);
+        msg = String.format("Invalid type definition for type %s: %s",
+                beanDesc, _format(msg, msgArgs));
+        throw InvalidDefinitionException.from(getGenerator(), msg, bean, null);
     }
 
     /**
@@ -1171,21 +1171,98 @@
      * @since 2.9
      */
     public <T> T reportBadPropertyDefinition(BeanDescription bean, BeanPropertyDefinition prop,
-            String message, Object... args) throws JsonMappingException {
-        if (args != null && args.length > 0) {
-            message = String.format(message, args);
+            String message, Object... msgArgs) throws JsonMappingException {
+        message = _format(message, msgArgs);
+        String propName = "N/A";
+        if (prop != null) {
+            propName = _quotedString(prop.getName());
         }
-        String propName = (prop == null)  ? "N/A" : _quotedString(prop.getName());
-        String beanDesc = (bean == null) ? "N/A" : _desc(bean.getType().getGenericSignature());
-        throw mappingException("Invalid definition for property %s (of type %s): %s",
+        String beanDesc = "N/A";
+        if (bean != null) {
+            beanDesc = ClassUtil.nameOf(bean.getBeanClass());
+        }
+        message = String.format("Invalid definition for property %s (of type %s): %s",
                 propName, beanDesc, message);
+        throw InvalidDefinitionException.from(getGenerator(), message, bean, prop);
+    }
+
+    @Override
+    public <T> T reportBadDefinition(JavaType type, String msg) throws JsonMappingException {
+        throw InvalidDefinitionException.from(getGenerator(), msg, type);
     }
 
     /**
+     * @since 2.9
+     */
+    public <T> T reportBadDefinition(JavaType type, String msg, Throwable cause)
+            throws JsonMappingException {
+        InvalidDefinitionException e = InvalidDefinitionException.from(getGenerator(), msg, type);
+        e.initCause(cause);
+        throw e;
+    }
+
+    /**
+     * @since 2.9
+     */
+    public <T> T reportBadDefinition(Class<?> raw, String msg, Throwable cause)
+            throws JsonMappingException {
+        InvalidDefinitionException e = InvalidDefinitionException.from(getGenerator(), msg, constructType(raw));
+        e.initCause(cause);
+        throw e;
+    }
+
+    /**
+     * Helper method called to indicate problem; default behavior is to construct and
+     * throw a {@link JsonMappingException}, but in future may collect more than one
+     * and only throw after certain number, or at the end of serialization.
+     *
      * @since 2.8
      */
-    public JsonGenerator getGenerator() {
-        return null;
+    public void reportMappingProblem(Throwable t, String message, Object... msgArgs) throws JsonMappingException {
+        message = _format(message, msgArgs);
+        throw JsonMappingException.from(getGenerator(), message, t);
+    }
+
+    @Override
+    public JsonMappingException invalidTypeIdException(JavaType baseType, String typeId,
+            String extraDesc) {
+        String msg = String.format("Could not resolve type id '%s' as a subtype of %s",
+                typeId, baseType);
+        return InvalidTypeIdException.from(null, _colonConcat(msg, extraDesc), baseType, typeId);
+    }
+
+    /*
+    /********************************************************
+    /* Error reporting, deprecated methods
+    /********************************************************
+     */
+
+    /**
+     * Factory method for constructing a {@link JsonMappingException};
+     * usually only indirectly used by calling
+     * {@link #reportMappingProblem(String, Object...)}.
+     *
+     * @since 2.6
+     *
+     * @deprecated Since 2.9
+     */
+    @Deprecated // since 2.9
+    public JsonMappingException mappingException(String message, Object... msgArgs) {
+        return JsonMappingException.from(getGenerator(), _format(message, msgArgs));
+    }
+
+    /**
+     * Factory method for constructing a {@link JsonMappingException};
+     * usually only indirectly used by calling
+     * {@link #reportMappingProblem(Throwable, String, Object...)}
+     * 
+     * @since 2.8
+     *
+     * @deprecated Since 2.9
+     */
+    @Deprecated // since 2.9
+    protected JsonMappingException mappingException(Throwable t, String message, Object... msgArgs) {
+        return JsonMappingException.from(getGenerator(), _format(message, msgArgs), t);
     }
 
     /*
@@ -1204,14 +1281,15 @@
                 return;
             }
         }
-        reportMappingProblem("Incompatible types: declared root type (%s) vs %s",
-                rootType, value.getClass().getName());
+        reportBadDefinition(rootType, String.format(
+                "Incompatible types: declared root type (%s) vs %s",
+                rootType, ClassUtil.classNameOf(value)));
     }
 
     /**
      * Method that will try to find a serializer, either from cache
      * or by constructing one; but will not return an "unknown" serializer
-     * if this can not be done but rather returns null.
+     * if this cannot be done but rather returns null.
      *
      * @return Serializer if one can be found, null if not.
      */
@@ -1260,8 +1338,8 @@
             /* We better only expose checked exceptions, since those
              * are what caller is expected to handle
              */
+            ser = null; // doesn't matter but compiler whines otherwise
             reportMappingProblem(iae, iae.getMessage());
-            return null; // never gets here
         }
 
         if (ser != null) {
@@ -1278,11 +1356,10 @@
         try {
             ser = _createUntypedSerializer(type);
         } catch (IllegalArgumentException iae) {
-            /* We better only expose checked exceptions, since those
-             * are what caller is expected to handle
-             */
+            // We better only expose checked exceptions, since those
+            // are what caller is expected to handle
+            ser = null;
             reportMappingProblem(iae, iae.getMessage());
-            return null; // never gets here
         }
     
         if (ser != null) {
@@ -1303,6 +1380,10 @@
          *    since there's one instance per serialization).
          *   Perhaps not-yet-resolved instance might be exposed too early to callers.
          */
+        // 13-Apr-2018, tatu: Problem does NOT occur any more with late 2.8.x and 2.9.x
+        //    versions, likely due to concurrency fixes for `AnnotatedClass` introspection.
+        //    This sync block could probably be removed; but to minimize any risk of
+        //    regression sync block will only be removed from 3.0.
         synchronized (_serializerCache) {
             // 17-Feb-2013, tatu: Used to call deprecated method (that passed property)
             return (JsonSerializer<Object>)_serializerFactory.createSerializer(this, type);
@@ -1340,26 +1421,12 @@
     /**********************************************************
      */
 
-    protected String _desc(Object value) {
-        if (value == null) {
-            return "N/A";
-        }
-        return "'"+value+"'";
-    }
-
-    protected String _quotedString(Object value) {
-        if (value == null) {
-            return "N/A";
-        }
-        return String.valueOf(value);
-    }
-
     protected final DateFormat _dateFormat()
     {
         if (_dateFormat != null) {
             return _dateFormat;
         }
-        /* At this point, all timezone configuration should have occured, with respect
+        /* At this point, all timezone configuration should have occurred, with respect
          * to default dateformat configuration. But we still better clone
          * an instance as formatters are stateful, not thread-safe.
          */
diff --git a/src/main/java/com/fasterxml/jackson/databind/annotation/JsonPOJOBuilder.java b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonPOJOBuilder.java
index 3bab933..2c6841d 100644
--- a/src/main/java/com/fasterxml/jackson/databind/annotation/JsonPOJOBuilder.java
+++ b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonPOJOBuilder.java
@@ -31,52 +31,66 @@
 @com.fasterxml.jackson.annotation.JacksonAnnotation
 public @interface JsonPOJOBuilder
 {
-	/**
-	 * Property to use for re-defining which zero-argument method
-	 * is considered the actual "build-method": method called after
-	 * all data has been bound, and the actual instance needs to
-	 * be instantiated.
-	 *<p>
-	 * Default value is "build".
-	 */
-	public String buildMethodName() default "build";
+    /**
+     * @since 2.9
+     */
+    public final static String DEFAULT_BUILD_METHOD = "build";
 
-	/**
-	 * Property used for (re)defining name prefix to use for
-	 * auto-detecting "with-methods": methods that are similar to
-	 * "set-methods" (in that they take an argument), but that
-	 * may also return the new builder instance to use
-	 * (which may be 'this', or a new modified builder instance).
-	 * Note that in addition to this prefix, it is also possible
-	 * to use {@link com.fasterxml.jackson.annotation.JsonProperty}
-	 * annotation to indicate "with-methods" (as well as
-	 * {@link com.fasterxml.jackson.annotation.JsonSetter}).
-	 *<p>
-	 * Default value is "with", so that method named "withValue()"
-	 * would be used for binding JSON property "value" (using type
-	 * indicated by the argument; or one defined with annotations.
-	 */
-	public String withPrefix() default "with";
+    /**
+     * @since 2.9
+     */
+    public final static String DEFAULT_WITH_PREFIX = "with";
+
+    /**
+     * Property to use for re-defining which zero-argument method
+     * is considered the actual "build-method": method called after
+     * all data has been bound, and the actual instance needs to
+     * be instantiated.
+     *<p>
+     * Default value is "build".
+     */
+    public String buildMethodName() default DEFAULT_BUILD_METHOD;
+
+    /**
+     * Property used for (re)defining name prefix to use for
+     * auto-detecting "with-methods": methods that are similar to
+     * "set-methods" (in that they take an argument), but that
+     * may also return the new builder instance to use
+     * (which may be 'this', or a new modified builder instance).
+     * Note that in addition to this prefix, it is also possible
+     * to use {@link com.fasterxml.jackson.annotation.JsonProperty}
+     * annotation to indicate "with-methods" (as well as
+     * {@link com.fasterxml.jackson.annotation.JsonSetter}).
+     *<p>
+     * Default value is "with", so that method named "withValue()"
+     * would be used for binding JSON property "value" (using type
+     * indicated by the argument; or one defined with annotations.
+     */
+    public String withPrefix() default DEFAULT_WITH_PREFIX;
 
     /*
     /**********************************************************
     /* Helper classes
     /**********************************************************
      */
-	
-	/**
-	 * Simple value container for containing values read from
-	 * {@link JsonPOJOBuilder} annotation instance.
-	 */
-	public class Value
-	{
-        public final String buildMethodName;
-	    public final String withPrefix;
 
-	    public Value(JsonPOJOBuilder ann)
-	    {
-	        buildMethodName = ann.buildMethodName();
-	        withPrefix = ann.withPrefix();
-	    }
-	}
+    /**
+     * Simple value container for containing values read from
+     * {@link JsonPOJOBuilder} annotation instance.
+     */
+    public class Value
+    {
+        public final String buildMethodName;
+        public final String withPrefix;
+
+        public Value(JsonPOJOBuilder ann) {
+            this(ann.buildMethodName(), ann.withPrefix());
+        }
+
+        public Value(String buildMethodName, String withPrefix)
+        {
+            this.buildMethodName = buildMethodName;
+            this.withPrefix = withPrefix;
+        }
+    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/annotation/JsonSerialize.java b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonSerialize.java
index e48b578..19767fe 100644
--- a/src/main/java/com/fasterxml/jackson/databind/annotation/JsonSerialize.java
+++ b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonSerialize.java
@@ -130,7 +130,7 @@
     /**
      * Which helper object is to be used to convert type into something
      * that Jackson knows how to serialize; either because base type
-     * can not be serialized easily, or just to alter serialization.
+     * cannot be serialized easily, or just to alter serialization.
      *
      * @since 2.2
      */
diff --git a/src/main/java/com/fasterxml/jackson/databind/annotation/NoClass.java b/src/main/java/com/fasterxml/jackson/databind/annotation/NoClass.java
index 86055b9..ff8ebae 100644
--- a/src/main/java/com/fasterxml/jackson/databind/annotation/NoClass.java
+++ b/src/main/java/com/fasterxml/jackson/databind/annotation/NoClass.java
@@ -2,7 +2,7 @@
 
 /**
  * Marker class used with annotations to indicate "no class". This is
- * a silly but necessary work-around -- annotations can not take nulls
+ * a silly but necessary work-around -- annotations cannot take nulls
  * as either default or explicit values. Hence for class values we must
  * explicitly use a bogus placeholder to denote equivalent of
  * "no class" (for which 'null' is usually the natural choice).
diff --git a/src/main/java/com/fasterxml/jackson/databind/annotation/package-info.java b/src/main/java/com/fasterxml/jackson/databind/annotation/package-info.java
index 3a73fd8..3025002 100644
--- a/src/main/java/com/fasterxml/jackson/databind/annotation/package-info.java
+++ b/src/main/java/com/fasterxml/jackson/databind/annotation/package-info.java
@@ -1,7 +1,7 @@
 /**
  * Annotations that directly depend on classes in databinding bundle
- * (not just Jackson core) and can not be included
- * in Jackson core annotations package (because it can not have any
+ * (not just Jackson core) and cannot be included
+ * in Jackson core annotations package (because it cannot have any
  * external dependencies).
  */
 package com.fasterxml.jackson.databind.annotation;
diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/BaseSettings.java b/src/main/java/com/fasterxml/jackson/databind/cfg/BaseSettings.java
index 3421e87..e289ab6 100644
--- a/src/main/java/com/fasterxml/jackson/databind/cfg/BaseSettings.java
+++ b/src/main/java/com/fasterxml/jackson/databind/cfg/BaseSettings.java
@@ -4,13 +4,10 @@
 import java.util.Locale;
 import java.util.TimeZone;
 
-import com.fasterxml.jackson.annotation.JsonAutoDetect;
-import com.fasterxml.jackson.annotation.PropertyAccessor;
 import com.fasterxml.jackson.core.Base64Variant;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair;
 import com.fasterxml.jackson.databind.introspect.ClassIntrospector;
-import com.fasterxml.jackson.databind.introspect.VisibilityChecker;
 import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
 import com.fasterxml.jackson.databind.type.TypeFactory;
 import com.fasterxml.jackson.databind.util.StdDateFormat;
@@ -55,17 +52,6 @@
     protected final AnnotationIntrospector _annotationIntrospector;
 
     /**
-     * Object used for determining whether specific property elements
-     * (method, constructors, fields) can be auto-detected based on
-     * their visibility (access modifiers). Can be changed to allow
-     * different minimum visibility levels for auto-detection. Note
-     * that this is the global handler; individual types (classes)
-     * can further override active checker used (using
-     * {@link JsonAutoDetect} annotation)
-     */
-    protected final VisibilityChecker<?> _visibilityChecker;
-
-    /**
      * Custom property naming strategy in use, if any.
      */
     protected final PropertyNamingStrategy _propertyNamingStrategy;
@@ -145,13 +131,12 @@
      */
 
     public BaseSettings(ClassIntrospector ci, AnnotationIntrospector ai,
-            VisibilityChecker<?> vc, PropertyNamingStrategy pns, TypeFactory tf,
+            PropertyNamingStrategy pns, TypeFactory tf,
             TypeResolverBuilder<?> typer, DateFormat dateFormat, HandlerInstantiator hi,
             Locale locale, TimeZone tz, Base64Variant defaultBase64)
     {
         _classIntrospector = ci;
         _annotationIntrospector = ai;
-        _visibilityChecker = vc;
         _propertyNamingStrategy = pns;
         _typeFactory = tf;
         _typeResolverBuilder = typer;
@@ -162,6 +147,26 @@
         _defaultBase64 = defaultBase64;
     }
 
+    /**
+     * Turns out we are not necessarily 100% stateless, alas, since {@link ClassIntrospector}
+     * typically has a cache. So this method is needed for deep copy() of Mapper.
+     *
+     * @since 2.9.6
+     */
+    public BaseSettings copy() {
+        return new BaseSettings(_classIntrospector.copy(),
+            _annotationIntrospector,
+            _propertyNamingStrategy,
+            _typeFactory,
+            _typeResolverBuilder,
+            _dateFormat,
+            _handlerInstantiator,
+            _locale,
+            _timeZone,
+            _defaultBase64);
+
+    }
+
     /*
     /**********************************************************
     /* Factory methods
@@ -172,7 +177,7 @@
         if (_classIntrospector == ci) {
             return this;
         }
-        return new BaseSettings(ci, _annotationIntrospector, _visibilityChecker, _propertyNamingStrategy, _typeFactory,
+        return new BaseSettings(ci, _annotationIntrospector, _propertyNamingStrategy, _typeFactory,
                 _typeResolverBuilder, _dateFormat, _handlerInstantiator, _locale,
                 _timeZone, _defaultBase64);
     }
@@ -181,7 +186,7 @@
         if (_annotationIntrospector == ai) {
             return this;
         }
-        return new BaseSettings(_classIntrospector, ai, _visibilityChecker, _propertyNamingStrategy, _typeFactory,
+        return new BaseSettings(_classIntrospector, ai, _propertyNamingStrategy, _typeFactory,
                 _typeResolverBuilder, _dateFormat, _handlerInstantiator, _locale,
                 _timeZone, _defaultBase64);
     }
@@ -193,16 +198,8 @@
     public BaseSettings withAppendedAnnotationIntrospector(AnnotationIntrospector ai) {
         return withAnnotationIntrospector(AnnotationIntrospectorPair.create(_annotationIntrospector, ai));
     }
-    
-    public BaseSettings withVisibilityChecker(VisibilityChecker<?> vc) {
-        if (_visibilityChecker == vc) {
-            return this;
-        }
-        return new BaseSettings(_classIntrospector, _annotationIntrospector, vc, _propertyNamingStrategy, _typeFactory,
-                _typeResolverBuilder, _dateFormat, _handlerInstantiator, _locale,
-                _timeZone, _defaultBase64);
-    }
 
+    /*
     public BaseSettings withVisibility(PropertyAccessor forMethod, JsonAutoDetect.Visibility visibility) {
         return new BaseSettings(_classIntrospector, _annotationIntrospector,
                 _visibilityChecker.withVisibility(forMethod, visibility),
@@ -210,12 +207,13 @@
                 _typeResolverBuilder, _dateFormat, _handlerInstantiator, _locale,
                 _timeZone, _defaultBase64);
     }
+    */
     
     public BaseSettings withPropertyNamingStrategy(PropertyNamingStrategy pns) {
         if (_propertyNamingStrategy == pns) {
             return this;
         }
-        return new BaseSettings(_classIntrospector, _annotationIntrospector, _visibilityChecker, pns, _typeFactory,
+        return new BaseSettings(_classIntrospector, _annotationIntrospector, pns, _typeFactory,
                 _typeResolverBuilder, _dateFormat, _handlerInstantiator, _locale,
                 _timeZone, _defaultBase64);
     }
@@ -224,7 +222,7 @@
         if (_typeFactory == tf) {
             return this;
         }
-        return new BaseSettings(_classIntrospector, _annotationIntrospector, _visibilityChecker, _propertyNamingStrategy, tf,
+        return new BaseSettings(_classIntrospector, _annotationIntrospector, _propertyNamingStrategy, tf,
                 _typeResolverBuilder, _dateFormat, _handlerInstantiator, _locale,
                 _timeZone, _defaultBase64);
     }
@@ -233,7 +231,7 @@
         if (_typeResolverBuilder == typer) {
             return this;
         }
-        return new BaseSettings(_classIntrospector, _annotationIntrospector, _visibilityChecker, _propertyNamingStrategy, _typeFactory,
+        return new BaseSettings(_classIntrospector, _annotationIntrospector, _propertyNamingStrategy, _typeFactory,
                 typer, _dateFormat, _handlerInstantiator, _locale,
                 _timeZone, _defaultBase64);
     }
@@ -247,7 +245,7 @@
         if ((df != null) && hasExplicitTimeZone()) {
             df = _force(df, _timeZone);
         }
-        return new BaseSettings(_classIntrospector, _annotationIntrospector, _visibilityChecker, _propertyNamingStrategy, _typeFactory,
+        return new BaseSettings(_classIntrospector, _annotationIntrospector, _propertyNamingStrategy, _typeFactory,
                 _typeResolverBuilder, df, _handlerInstantiator, _locale,
                 _timeZone, _defaultBase64);
     }
@@ -256,7 +254,7 @@
         if (_handlerInstantiator == hi) {
             return this;
         }
-        return new BaseSettings(_classIntrospector, _annotationIntrospector, _visibilityChecker, _propertyNamingStrategy, _typeFactory,
+        return new BaseSettings(_classIntrospector, _annotationIntrospector, _propertyNamingStrategy, _typeFactory,
                 _typeResolverBuilder, _dateFormat, hi, _locale,
                 _timeZone, _defaultBase64);
     }
@@ -265,7 +263,7 @@
         if (_locale == l) {
             return this;
         }
-        return new BaseSettings(_classIntrospector, _annotationIntrospector, _visibilityChecker, _propertyNamingStrategy, _typeFactory,
+        return new BaseSettings(_classIntrospector, _annotationIntrospector, _propertyNamingStrategy, _typeFactory,
                 _typeResolverBuilder, _dateFormat, _handlerInstantiator, l,
                 _timeZone, _defaultBase64);
     }
@@ -286,7 +284,7 @@
         
         DateFormat df = _force(_dateFormat, tz);
         return new BaseSettings(_classIntrospector, _annotationIntrospector,
-                _visibilityChecker, _propertyNamingStrategy, _typeFactory,
+                _propertyNamingStrategy, _typeFactory,
                 _typeResolverBuilder, df, _handlerInstantiator, _locale,
                 tz, _defaultBase64);
     }
@@ -299,7 +297,7 @@
             return this;
         }
         return new BaseSettings(_classIntrospector, _annotationIntrospector,
-                _visibilityChecker, _propertyNamingStrategy, _typeFactory,
+                _propertyNamingStrategy, _typeFactory,
                 _typeResolverBuilder, _dateFormat, _handlerInstantiator, _locale,
                 _timeZone, base64);
     }
@@ -318,10 +316,6 @@
         return _annotationIntrospector;
     }
 
-    public VisibilityChecker<?> getVisibilityChecker() {
-        return _visibilityChecker;
-    }
-
     public PropertyNamingStrategy getPropertyNamingStrategy() {
         return _propertyNamingStrategy;
     }
diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/ConfigFeature.java b/src/main/java/com/fasterxml/jackson/databind/cfg/ConfigFeature.java
index daf9f25..91edeca 100644
--- a/src/main/java/com/fasterxml/jackson/databind/cfg/ConfigFeature.java
+++ b/src/main/java/com/fasterxml/jackson/databind/cfg/ConfigFeature.java
@@ -3,7 +3,7 @@
 /**
  * Interface that actual SerializationFeature enumerations used by
  * {@link MapperConfig} implementations must implement.
- * Necessary since enums can not be extended using normal
+ * Necessary since enums cannot be extended using normal
  * inheritance, but can implement interfaces
  */
 public interface ConfigFeature
diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/ConfigOverride.java b/src/main/java/com/fasterxml/jackson/databind/cfg/ConfigOverride.java
index 18e6304..7557d9e 100644
--- a/src/main/java/com/fasterxml/jackson/databind/cfg/ConfigOverride.java
+++ b/src/main/java/com/fasterxml/jackson/databind/cfg/ConfigOverride.java
@@ -1,8 +1,10 @@
 package com.fasterxml.jackson.databind.cfg;
 
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonSetter;
 
 /**
  * Configuration object that is accessed by databinding functionality
@@ -21,37 +23,115 @@
     protected JsonFormat.Value _format;
 
     /**
-     * Definitions of inclusion overrides, if any.
+     * Definitions of inclusion defaults to use for properties included in this POJO type.
+     * Overrides global defaults, may be overridden by per-property-type (see
+     * {@link #_includeAsProperty}) and per-property overrides (annotations).
      */
     protected JsonInclude.Value _include;
 
     /**
+     * Definitions of inclusion defaults for properties of this specified type (regardless
+     * of POJO in which they are included).
+     * Overrides global defaults, per-POJO inclusion defaults (see {#link {@link #_include}}),
+     * may be overridden by per-property overrides.
+     *
+     * @since 2.9
+     */
+    protected JsonInclude.Value _includeAsProperty;
+
+    /**
      * Definitions of property ignoral (whether to serialize, deserialize
      * given logical property) overrides, if any.
      */
     protected JsonIgnoreProperties.Value _ignorals;
 
     /**
+     * Definitions of setter overrides regarding null handling
+     *
+     * @since 2.9
+     */
+    protected JsonSetter.Value _setterInfo;
+
+    /**
+     * Overrides for auto-detection visibility rules for this type.
+     *
+     * @since 2.9
+     */
+    protected JsonAutoDetect.Value _visibility;
+
+    /**
      * Flag that indicates whether "is ignorable type" is specified for this type;
      * and if so, is it to be ignored (true) or not ignored (false); `null` is
      * used to indicate "not specified", in which case other configuration (class
      * annotation) is used.
      */
     protected Boolean _isIgnoredType;
+
+    /**
+     * Flag that indicates whether properties of this type default to being merged
+     * or not.
+     */
+    protected Boolean _mergeable;
     
     protected ConfigOverride() { }
     protected ConfigOverride(ConfigOverride src) {
         _format = src._format;
         _include = src._include;
+        _includeAsProperty = src._includeAsProperty;
         _ignorals = src._ignorals;
+        _setterInfo = src._setterInfo;
+        _visibility = src._visibility;
         _isIgnoredType = src._isIgnoredType;
+        _mergeable = src._mergeable;
+    }
+
+    /**
+     * Accessor for immutable "empty" instance that has no configuration overrides defined.
+     *
+     * @since 2.9
+     */
+    public static ConfigOverride empty() {
+        return Empty.INSTANCE;
     }
 
     public JsonFormat.Value getFormat() { return _format; }
     public JsonInclude.Value getInclude() { return _include; }
+
+    /**
+     * @since 2.9
+     */
+    public JsonInclude.Value getIncludeAsProperty() { return _includeAsProperty; }
+
     public JsonIgnoreProperties.Value getIgnorals() { return _ignorals; }
 
     public Boolean getIsIgnoredType() {
         return _isIgnoredType;
     }
+    
+    /**
+     * @since 2.9
+     */
+    public JsonSetter.Value getSetterInfo() { return _setterInfo; }
+
+    /**
+     * @since 2.9
+     */
+    public JsonAutoDetect.Value getVisibility() { return _visibility; }
+
+    /**
+     * @since 2.9
+     */
+    public Boolean getMergeable() { return _mergeable; }
+    
+    /**
+     * Implementation used solely for "empty" instance; has no mutators
+     * and is not changed by core functionality.
+     *
+     * @since 2.9
+     */
+    final static class Empty extends ConfigOverride {
+        final static Empty INSTANCE = new Empty();
+
+        private Empty() { }
+    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/ConfigOverrides.java b/src/main/java/com/fasterxml/jackson/databind/cfg/ConfigOverrides.java
index 9fd944e..49c622c 100644
--- a/src/main/java/com/fasterxml/jackson/databind/cfg/ConfigOverrides.java
+++ b/src/main/java/com/fasterxml/jackson/databind/cfg/ConfigOverrides.java
@@ -2,6 +2,10 @@
 
 import java.util.*;
 
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonSetter;
+import com.fasterxml.jackson.databind.introspect.VisibilityChecker;
+
 /**
  * Container for individual {@link ConfigOverride} values.
  * 
@@ -12,28 +16,82 @@
 {
     private static final long serialVersionUID = 1L;
 
+    /**
+     * Per-type override definitions
+     */
     protected Map<Class<?>, MutableConfigOverride> _overrides;
 
+    // // // Global defaulting
+
+    /**
+     * @since 2.9
+     */
+    protected JsonInclude.Value _defaultInclusion;
+
+    /**
+     * @since 2.9
+     */
+    protected JsonSetter.Value _defaultSetterInfo;
+
+    /**
+     * @since 2.9
+     */
+    protected VisibilityChecker<?> _visibilityChecker;
+
+    /**
+     * @since 2.9
+     */
+    protected Boolean _defaultMergeable;
+
+    /*
+    /**********************************************************
+    /* Life cycle
+    /**********************************************************
+     */
+
     public ConfigOverrides() {
-        _overrides = null;
+        this(null,
+                // !!! TODO: change to (ALWAYS, ALWAYS)?
+                JsonInclude.Value.empty(),
+                JsonSetter.Value.empty(),
+                VisibilityChecker.Std.defaultInstance(),
+                null
+        );
     }
 
-    protected ConfigOverrides(Map<Class<?>, MutableConfigOverride> overrides) {
+    protected ConfigOverrides(Map<Class<?>, MutableConfigOverride> overrides,
+            JsonInclude.Value defIncl,
+            JsonSetter.Value defSetter,
+            VisibilityChecker<?> defVisibility,
+            Boolean defMergeable) {
         _overrides = overrides;
+        _defaultInclusion = defIncl;
+        _defaultSetterInfo = defSetter;
+        _visibilityChecker = defVisibility;
+        _defaultMergeable = defMergeable;
     }
 
     public ConfigOverrides copy()
     {
+        Map<Class<?>, MutableConfigOverride> newOverrides;
         if (_overrides == null) {
-            return new ConfigOverrides();
+            newOverrides = null;
+        } else {
+            newOverrides = _newMap();
+            for (Map.Entry<Class<?>, MutableConfigOverride> entry : _overrides.entrySet()) {
+                newOverrides.put(entry.getKey(), entry.getValue().copy());
+            }
         }
-        Map<Class<?>, MutableConfigOverride> newOverrides = _newMap();
-        for (Map.Entry<Class<?>, MutableConfigOverride> entry : _overrides.entrySet()) {
-            newOverrides.put(entry.getKey(), entry.getValue().copy());
-        }
-        return new ConfigOverrides(newOverrides);
+        return new ConfigOverrides(newOverrides,
+                _defaultInclusion, _defaultSetterInfo, _visibilityChecker, _defaultMergeable);
     }
 
+    /*
+    /**********************************************************
+    /* Per-type override access
+    /**********************************************************
+     */
+    
     public ConfigOverride findOverride(Class<?> type) {
         if (_overrides == null) {
             return null;
@@ -53,6 +111,65 @@
         return override;
     }
 
+    /*
+    /**********************************************************
+    /* Global defaults access
+    /**********************************************************
+     */
+
+    public JsonInclude.Value getDefaultInclusion() {
+        return _defaultInclusion;
+    }
+
+    public JsonSetter.Value getDefaultSetterInfo() {
+        return _defaultSetterInfo;
+    }
+
+    public Boolean getDefaultMergeable() {
+        return _defaultMergeable;
+    }
+
+    /**
+     * @since 2.9
+     */
+    public VisibilityChecker<?> getDefaultVisibility() {
+        return _visibilityChecker;
+    }
+
+    /**
+     * @since 2.9
+     */
+    public void setDefaultInclusion(JsonInclude.Value v) {
+        _defaultInclusion = v;
+    }
+
+    /**
+     * @since 2.9
+     */
+    public void setDefaultSetterInfo(JsonSetter.Value v) {
+        _defaultSetterInfo = v;
+    }
+
+    /**
+     * @since 2.9
+     */
+    public void setDefaultMergeable(Boolean v) {
+        _defaultMergeable = v;
+    }
+
+    /**
+     * @since 2.9
+     */
+    public void setDefaultVisibility(VisibilityChecker<?> v) {
+        _visibilityChecker = v;
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+    
     protected Map<Class<?>, MutableConfigOverride> _newMap() {
         return new HashMap<Class<?>, MutableConfigOverride>();
     }
diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/ContextAttributes.java b/src/main/java/com/fasterxml/jackson/databind/cfg/ContextAttributes.java
index a0170b9..521cf8b 100644
--- a/src/main/java/com/fasterxml/jackson/databind/cfg/ContextAttributes.java
+++ b/src/main/java/com/fasterxml/jackson/databind/cfg/ContextAttributes.java
@@ -69,7 +69,7 @@
         protected final static Object NULL_SURROGATE = new Object();
         
         /**
-         * Shared attributes that we can not modify in-place.
+         * Shared attributes that we cannot modify in-place.
          */
         protected final Map<?,?> _shared;
 
diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/DeserializerFactoryConfig.java b/src/main/java/com/fasterxml/jackson/databind/cfg/DeserializerFactoryConfig.java
index de0c6fe..20b048f 100644
--- a/src/main/java/com/fasterxml/jackson/databind/cfg/DeserializerFactoryConfig.java
+++ b/src/main/java/com/fasterxml/jackson/databind/cfg/DeserializerFactoryConfig.java
@@ -99,7 +99,7 @@
     public DeserializerFactoryConfig withAdditionalDeserializers(Deserializers additional)
     {
         if (additional == null) {
-            throw new IllegalArgumentException("Can not pass null Deserializers");
+            throw new IllegalArgumentException("Cannot pass null Deserializers");
         }
         Deserializers[] all = ArrayBuilders.insertInListNoDup(_additionalDeserializers, additional);
         return new DeserializerFactoryConfig(all, _additionalKeyDeserializers, _modifiers,
@@ -115,7 +115,7 @@
     public DeserializerFactoryConfig withAdditionalKeyDeserializers(KeyDeserializers additional)
     {
         if (additional == null) {
-            throw new IllegalArgumentException("Can not pass null KeyDeserializers");
+            throw new IllegalArgumentException("Cannot pass null KeyDeserializers");
         }
         KeyDeserializers[] all = ArrayBuilders.insertInListNoDup(_additionalKeyDeserializers, additional);
         return new DeserializerFactoryConfig(_additionalDeserializers, all, _modifiers,
@@ -131,7 +131,7 @@
     public DeserializerFactoryConfig withDeserializerModifier(BeanDeserializerModifier modifier)
     {
         if (modifier == null) {
-            throw new IllegalArgumentException("Can not pass null modifier");
+            throw new IllegalArgumentException("Cannot pass null modifier");
         }
         BeanDeserializerModifier[] all = ArrayBuilders.insertInListNoDup(_modifiers, modifier);
         return new DeserializerFactoryConfig(_additionalDeserializers, _additionalKeyDeserializers, all,
@@ -148,7 +148,7 @@
     public DeserializerFactoryConfig withAbstractTypeResolver(AbstractTypeResolver resolver)
     {
         if (resolver == null) {
-            throw new IllegalArgumentException("Can not pass null resolver");
+            throw new IllegalArgumentException("Cannot pass null resolver");
         }
         AbstractTypeResolver[] all = ArrayBuilders.insertInListNoDup(_abstractTypeResolvers, resolver);
         return new DeserializerFactoryConfig(_additionalDeserializers, _additionalKeyDeserializers, _modifiers,
@@ -168,7 +168,7 @@
     public DeserializerFactoryConfig withValueInstantiators(ValueInstantiators instantiators) 
     {
         if (instantiators == null) {
-            throw new IllegalArgumentException("Can not pass null resolver");
+            throw new IllegalArgumentException("Cannot pass null resolver");
         }
         ValueInstantiators[] all = ArrayBuilders.insertInListNoDup(_valueInstantiators, instantiators);
         return new DeserializerFactoryConfig(_additionalDeserializers, _additionalKeyDeserializers, _modifiers,
diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/HandlerInstantiator.java b/src/main/java/com/fasterxml/jackson/databind/cfg/HandlerInstantiator.java
index 2106979..757a379 100644
--- a/src/main/java/com/fasterxml/jackson/databind/cfg/HandlerInstantiator.java
+++ b/src/main/java/com/fasterxml/jackson/databind/cfg/HandlerInstantiator.java
@@ -5,6 +5,7 @@
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.deser.ValueInstantiator;
 import com.fasterxml.jackson.databind.introspect.Annotated;
+import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
 import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
 import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
 import com.fasterxml.jackson.databind.ser.VirtualBeanPropertyWriter;
@@ -160,4 +161,22 @@
             Class<?> implClass) {
         return null;
     }
+
+    /**
+     * Method called to construct a Filter (any Object with implementation of
+     * <code>equals(Object)</code> that determines if given value is to be
+     * excluded (true) or included (false)) to be used based on
+     * {@link com.fasterxml.jackson.annotation.JsonInclude} annotation (or
+     * equivalent).
+     *<p> 
+     * Default implementation returns `null` to indicate that default instantiation
+     * (use zero-arg constructor of the <code>filterClass</code>) should be
+     * used.
+     *
+     * @since 2.9
+     */
+    public Object includeFilterInstance(SerializationConfig config,
+            BeanPropertyDefinition forProperty, Class<?> filterClass) {
+        return null;
+    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfig.java b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfig.java
index 1ae5732..7bc3e7f 100644
--- a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfig.java
+++ b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfig.java
@@ -13,6 +13,7 @@
 import com.fasterxml.jackson.databind.introspect.Annotated;
 import com.fasterxml.jackson.databind.introspect.AnnotatedClass;
 import com.fasterxml.jackson.databind.introspect.ClassIntrospector;
+import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector;
 import com.fasterxml.jackson.databind.introspect.VisibilityChecker;
 import com.fasterxml.jackson.databind.jsontype.SubtypeResolver;
 import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
@@ -36,7 +37,7 @@
     implements ClassIntrospector.MixInResolver,
         java.io.Serializable
 {
-    private static final long serialVersionUID = 1L; // since 2.5
+    private static final long serialVersionUID = 2L; // since 2.9
 
     /**
      * @since 2.7
@@ -57,7 +58,7 @@
      * Immutable container object for simple configuration settings.
      */
     protected final BaseSettings _base;
-    
+
     /*
     /**********************************************************
     /* Life-cycle: constructors
@@ -232,22 +233,12 @@
      * Non-final since it is actually overridden by sub-classes (for now?)
      */
     public AnnotationIntrospector getAnnotationIntrospector() {
-        return _base.getAnnotationIntrospector();
+        if (isEnabled(MapperFeature.USE_ANNOTATIONS)) {
+            return _base.getAnnotationIntrospector();
+        }
+        return NopAnnotationIntrospector.instance;
     }
 
-    /**
-     * Accessor for object used for determining whether specific property elements
-     * (method, constructors, fields) can be auto-detected based on
-     * their visibility (access modifiers). Can be changed to allow
-     * different minimum visibility levels for auto-detection. Note
-     * that this is the global handler; individual types (classes)
-     * can further override active checker used (using
-     * {@link JsonAutoDetect} annotation)
-     */
-    public VisibilityChecker<?> getDefaultVisibilityChecker() {
-        return _base.getVisibilityChecker();
-    }
-    
     public final PropertyNamingStrategy getPropertyNamingStrategy() {
         return _base.getPropertyNamingStrategy();
     }
@@ -255,7 +246,7 @@
     public final HandlerInstantiator getHandlerInstantiator() {
         return _base.getHandlerInstantiator();
     }
-    
+
     /*
     /**********************************************************
     /* Configuration: type and subtype handling
@@ -319,12 +310,14 @@
     public BeanDescription introspectClassAnnotations(Class<?> cls) {
         return introspectClassAnnotations(constructType(cls));
     }
-    
+
     /**
      * Accessor for getting bean description that only contains class
      * annotations: useful if no getter/setter/creator information is needed.
      */
-    public abstract BeanDescription introspectClassAnnotations(JavaType type);
+    public BeanDescription introspectClassAnnotations(JavaType type) {
+        return getClassIntrospector().forClassAnnotations(this, type, this);
+    }
 
     /**
      * Accessor for getting bean description that only contains immediate class
@@ -334,12 +327,15 @@
     public BeanDescription introspectDirectClassAnnotations(Class<?> cls) {
         return introspectDirectClassAnnotations(constructType(cls));
     }
+
     /**
      * Accessor for getting bean description that only contains immediate class
      * annotations: ones from the class, and its direct mix-in, if any, but
      * not from super types.
      */
-    public abstract BeanDescription introspectDirectClassAnnotations(JavaType type);
+    public final BeanDescription introspectDirectClassAnnotations(JavaType type) {
+        return getClassIntrospector().forDirectClassAnnotations(this, type, this);
+    }
 
     /*
     /**********************************************************
@@ -348,6 +344,33 @@
      */
 
     /**
+     * Accessor for finding {@link ConfigOverride} to use for
+     * properties of given type, if any exist; or return `null` if not.
+     *<p>
+     * Note that only directly associated override
+     * is found; no type hierarchy traversal is performed.
+     *
+     * @since 2.8
+     * 
+     * @return Override object to use for the type, if defined; null if none.
+     */
+    public abstract ConfigOverride findConfigOverride(Class<?> type);
+
+    /**
+     * Accessor for finding {@link ConfigOverride} to use for
+     * properties of given type, if any exist; or if none, return an immutable
+     * "empty" instance with no overrides.
+     *<p>
+     * Note that only directly associated override
+     * is found; no type hierarchy traversal is performed.
+     *
+     * @since 2.9
+     * 
+     * @return Override object to use for the type, never null (but may be empty)
+     */
+    public abstract ConfigOverride getConfigOverride(Class<?> type);
+
+    /**
      * Accessor for default property inclusion to use for serialization,
      * used unless overridden by per-type or per-property overrides.
      *
@@ -374,8 +397,52 @@
      * 
      * @since 2.8.2
      */
-    public abstract JsonInclude.Value getDefaultPropertyInclusion(Class<?> baseType,
-            JsonInclude.Value defaultIncl);
+    public JsonInclude.Value getDefaultPropertyInclusion(Class<?> baseType,
+            JsonInclude.Value defaultIncl)
+    {
+        JsonInclude.Value v = getConfigOverride(baseType).getInclude();
+        if (v != null) {
+            return v;
+        }
+        return defaultIncl;
+    }
+
+    /**
+     * Accessor for default property inclusion to use for serialization,
+     * considering possible per-type override for given base type and
+     * possible per-type override for given property type.<br>
+     * NOTE: if no override found, defaults to value returned by
+     * {@link #getDefaultPropertyInclusion()}.
+     *
+     * @param baseType Type of the instance containing the targeted property.
+     * @param propertyType Type of the property to look up inclusion setting for.
+     *
+     * @since 2.9
+     */
+    public abstract JsonInclude.Value getDefaultInclusion(Class<?> baseType,
+            Class<?> propertyType);
+
+    /**
+     * Accessor for default property inclusion to use for serialization,
+     * considering possible per-type override for given base type and
+     * possible per-type override for given property type; but
+     * if none found, returning given <code>defaultIncl</code>
+     *
+     * @param baseType Type of the instance containing the targeted property.
+     * @param propertyType Type of the property to look up inclusion setting for.
+     * @param defaultIncl Inclusion setting to return if no overrides found.
+     *
+     * @since 2.9
+     */
+    public JsonInclude.Value getDefaultInclusion(Class<?> baseType,
+            Class<?> propertyType, JsonInclude.Value defaultIncl)
+    {
+        JsonInclude.Value baseOverride = getConfigOverride(baseType).getInclude();
+        JsonInclude.Value propOverride = getConfigOverride(propertyType).getIncludeAsProperty();
+
+        JsonInclude.Value result = JsonInclude.Value.mergeAll(defaultIncl, baseOverride, propOverride);
+        return result;
+    }
 
     /**
      * Accessor for default format settings to use for serialization (and, to a degree
@@ -406,15 +473,60 @@
             AnnotatedClass actualClass);
 
     /**
-     * Accessor for finding possible {@link ConfigOverride} to use for
-     * properties of given type. Note that only directly associate override
-     * is found; no type hierarchy traversal is performed.
-     *
-     * @since 2.8
-     * 
-     * @return Override object if there is an override for specified type; `null` if not
+     * Accessor for object used for determining whether specific property elements
+     * (method, constructors, fields) can be auto-detected based on
+     * their visibility (access modifiers). Can be changed to allow
+     * different minimum visibility levels for auto-detection. Note
+     * that this is the global handler; individual types (classes)
+     * can further override active checker used (using
+     * {@link JsonAutoDetect} annotation)
      */
-    public abstract ConfigOverride findConfigOverride(Class<?> type);
+    public abstract VisibilityChecker<?> getDefaultVisibilityChecker();
+
+    /**
+     * Accessor for object used for determining whether specific property elements
+     * (method, constructors, fields) can be auto-detected based on
+     * their visibility (access modifiers). This is based on global defaults
+     * (as would be returned by {@link #getDefaultVisibilityChecker()}, but
+     * then modified by possible class annotation (see {@link JsonAutoDetect})
+     * and/or per-type config override (see {@link ConfigOverride#getVisibility()}).
+     *
+     * @since 2.9
+     */
+    public abstract VisibilityChecker<?> getDefaultVisibilityChecker(Class<?> baseType,
+            AnnotatedClass actualClass);
+
+    /**
+     * Accessor for the baseline setter info used as the global baseline,
+     * not considering possible per-type overrides.
+     *
+     * @return Global base settings; never null
+     *
+     * @since 2.9
+     */
+    public abstract JsonSetter.Value getDefaultSetterInfo();
+
+    /**
+     * Accessor for the baseline merge info used as the global baseline,
+     * not considering possible per-type overrides.
+     *
+     * @return Global base settings, if any; `null` if none.
+     *
+     * @since 2.9
+     */
+    public abstract Boolean getDefaultMergeable();
+
+    /**
+     * Accessor for the baseline merge info used for given type, including global
+     * defaults if no type-specific overrides defined.
+     *
+     * @return Type-specific settings (if any); global defaults (same as
+     *    {@link #getDefaultMergeable()}) otherwise, if any defined; or `null`
+     *    if neither defined
+     *
+     * @since 2.9
+     */
+    public abstract Boolean getDefaultMergeable(Class<?> baseType);
 
     /*
     /**********************************************************
diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java
index 6f90f9b..a7ca2e5 100644
--- a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java
+++ b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java
@@ -6,6 +6,7 @@
 import com.fasterxml.jackson.annotation.*;
 import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
 import com.fasterxml.jackson.core.Base64Variant;
+
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.introspect.ClassIntrospector;
 import com.fasterxml.jackson.databind.introspect.ClassIntrospector.MixInResolver;
@@ -23,8 +24,24 @@
     extends MapperConfig<T>
     implements java.io.Serializable
 {
+    /**
+     * @since 2.9
+     */
+    protected final static ConfigOverride EMPTY_OVERRIDE = ConfigOverride.empty();
+
     private final static int DEFAULT_MAPPER_FEATURES = collectFeatureDefaults(MapperFeature.class);
 
+    /**
+     * @since 2.9
+     */
+    private final static int AUTO_DETECT_MASK =
+            MapperFeature.AUTO_DETECT_FIELDS.getMask()
+            | MapperFeature.AUTO_DETECT_GETTERS.getMask()
+            | MapperFeature.AUTO_DETECT_IS_GETTERS.getMask()
+            | MapperFeature.AUTO_DETECT_SETTERS.getMask()
+            | MapperFeature.AUTO_DETECT_CREATORS.getMask()
+            ;
+
     /*
     /**********************************************************
     /* Immutable config
@@ -33,7 +50,7 @@
 
     /**
      * Mix-in annotation mappings to use, if any: immutable,
-     * can not be changed once defined.
+     * cannot be changed once defined.
      * 
      * @since 2.6
      */
@@ -102,8 +119,8 @@
      * @since 2.8
      */
     protected MapperConfigBase(BaseSettings base,
-            SubtypeResolver str, SimpleMixInResolver mixins,
-            RootNameLookup rootNames, ConfigOverrides configOverrides)
+            SubtypeResolver str, SimpleMixInResolver mixins, RootNameLookup rootNames,
+            ConfigOverrides configOverrides)
     {
         super(base, DEFAULT_MAPPER_FEATURES);
         _mixIns = mixins;
@@ -117,14 +134,25 @@
     }
 
     /**
-     * @deprecated Since 2.8, remove from 2.9 or later
+     * Copy constructor usually called to make a copy for use by
+     * ObjectMapper that is copied.
+     *
+     * @since 2.8
      */
-    @Deprecated
-    protected MapperConfigBase(BaseSettings base,
-            SubtypeResolver str, SimpleMixInResolver mixins,
-            RootNameLookup rootNames)
+    protected MapperConfigBase(MapperConfigBase<CFG,T> src,
+            SimpleMixInResolver mixins, RootNameLookup rootNames,
+            ConfigOverrides configOverrides)
     {
-        this(base, str, mixins, rootNames, null);
+        // 18-Apr-2018, tatu: [databind#1898] need to force copying of `ClassIntrospector`
+        //    (to clear its cache) to avoid leakage
+        super(src, src._base.copy());
+        _mixIns = mixins;
+        _subtypeResolver = src._subtypeResolver;
+        _rootNames = rootNames;
+        _rootName = src._rootName;
+        _view = src._view;
+        _attributes = src._attributes;
+        _configOverrides = configOverrides;
     }
 
     /**
@@ -215,7 +243,7 @@
         _attributes = src._attributes;
         _configOverrides = src._configOverrides;
     }
-    
+
     /**
      * @since 2.3
      */
@@ -231,58 +259,86 @@
         _configOverrides = src._configOverrides;
     }
 
+    /*
+    /**********************************************************
+    /* Abstract fluent factory methods to be implemented by subtypes
+    /**********************************************************
+     */
+
     /**
-     * @since 2.8
+     * @since 2.9 (in this case, demoted from sub-classes)
      */
-    protected MapperConfigBase(MapperConfigBase<CFG,T> src, SimpleMixInResolver mixins,
-            RootNameLookup rootNames, ConfigOverrides configOverrides)
-    {
-        super(src);
-        _mixIns = mixins;
-        _subtypeResolver = src._subtypeResolver;
-        _rootNames = rootNames;
-        _rootName = src._rootName;
-        _view = src._view;
-        _attributes = src._attributes;
-        _configOverrides = configOverrides;
-    }
+    protected abstract T _withBase(BaseSettings newBase);
+
+    /**
+     * @since 2.9 (in this case, demoted from sub-classes)
+     */
+    protected abstract T _withMapperFeatures(int mapperFeatures);
 
     /*
     /**********************************************************
-    /* Overrides
+    /* Additional shared fluent factory methods; features
     /**********************************************************
      */
-
-    // note: demoted in 2.8 from sub-classes, as there's no difference
+    
+    /**
+     * Fluent factory method that will construct and return a new configuration
+     * object instance with specified features enabled.
+     */
+    @SuppressWarnings("unchecked")
     @Override
-    public VisibilityChecker<?> getDefaultVisibilityChecker()
+    public final T with(MapperFeature... features)
     {
-        VisibilityChecker<?> vchecker = super.getDefaultVisibilityChecker();
-        // then global overrides (disabling)
-        if (!isEnabled(MapperFeature.AUTO_DETECT_SETTERS)) {
-            vchecker = vchecker.withSetterVisibility(Visibility.NONE);
+        int newMapperFlags = _mapperFeatures;
+        for (MapperFeature f : features) {
+            newMapperFlags |= f.getMask();
         }
-        if (!isEnabled(MapperFeature.AUTO_DETECT_CREATORS)) {
-            vchecker = vchecker.withCreatorVisibility(Visibility.NONE);
+        if (newMapperFlags == _mapperFeatures) {
+            return (T) this;
         }
-        if (!isEnabled(MapperFeature.AUTO_DETECT_GETTERS)) {
-            vchecker = vchecker.withGetterVisibility(Visibility.NONE);
+        return _withMapperFeatures(newMapperFlags);
+    }
+
+    /**
+     * Fluent factory method that will construct and return a new configuration
+     * object instance with specified features disabled.
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    public final T without(MapperFeature... features)
+    {
+        int newMapperFlags = _mapperFeatures;
+        for (MapperFeature f : features) {
+             newMapperFlags &= ~f.getMask();
         }
-        if (!isEnabled(MapperFeature.AUTO_DETECT_IS_GETTERS)) {
-            vchecker = vchecker.withIsGetterVisibility(Visibility.NONE);
+        if (newMapperFlags == _mapperFeatures) {
+            return (T) this;
         }
-        if (!isEnabled(MapperFeature.AUTO_DETECT_FIELDS)) {
-            vchecker = vchecker.withFieldVisibility(Visibility.NONE);
+        return _withMapperFeatures(newMapperFlags);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public final T with(MapperFeature feature, boolean state)
+    {
+        int newMapperFlags;
+        if (state) {
+            newMapperFlags = _mapperFeatures | feature.getMask();
+        } else {
+            newMapperFlags = _mapperFeatures & ~feature.getMask();
         }
-        return vchecker;
+        if (newMapperFlags == _mapperFeatures) {
+            return (T) this;
+        }
+        return _withMapperFeatures(newMapperFlags);
     }
 
     /*
     /**********************************************************
-    /* Addition fluent factory methods, common to all sub-types
+    /* Additional shared fluent factory methods; introspectors
     /**********************************************************
      */
-
+    
     /**
      * Method for constructing and returning a new instance with different
      * {@link AnnotationIntrospector} to use (replacing old one).
@@ -290,19 +346,25 @@
      * NOTE: make sure to register new instance with <code>ObjectMapper</code>
      * if directly calling this method.
      */
-    public abstract T with(AnnotationIntrospector ai);
+    public final T with(AnnotationIntrospector ai) {
+        return _withBase(_base.withAnnotationIntrospector(ai));
+    }
 
     /**
      * Method for constructing and returning a new instance with additional
      * {@link AnnotationIntrospector} appended (as the lowest priority one)
      */
-    public abstract T withAppendedAnnotationIntrospector(AnnotationIntrospector introspector);
+    public final T withAppendedAnnotationIntrospector(AnnotationIntrospector ai) {
+        return _withBase(_base.withAppendedAnnotationIntrospector(ai));
+    }
 
     /**
      * Method for constructing and returning a new instance with additional
      * {@link AnnotationIntrospector} inserted (as the highest priority one)
      */
-    public abstract T withInsertedAnnotationIntrospector(AnnotationIntrospector introspector);
+    public final T withInsertedAnnotationIntrospector(AnnotationIntrospector ai) {
+        return _withBase(_base.withInsertedAnnotationIntrospector(ai));
+    }
     
     /**
      * Method for constructing and returning a new instance with different
@@ -312,122 +374,15 @@
      * NOTE: make sure to register new instance with <code>ObjectMapper</code>
      * if directly calling this method.
      */
-    public abstract T with(ClassIntrospector ci);
-
-    /**
-     * Method for constructing and returning a new instance with different
-     * {@link DateFormat}
-     * to use.
-     *<p>
-     * NOTE: make sure to register new instance with <code>ObjectMapper</code>
-     * if directly calling this method.
-     */
-    public abstract T with(DateFormat df);
-
-    /**
-     * Method for constructing and returning a new instance with different
-     * {@link HandlerInstantiator}
-     * to use.
-     *<p>
-     * NOTE: make sure to register new instance with <code>ObjectMapper</code>
-     * if directly calling this method.
-     */
-    public abstract T with(HandlerInstantiator hi);
-    
-    /**
-     * Method for constructing and returning a new instance with different
-     * {@link PropertyNamingStrategy}
-     * to use.
-     *<p>
-     * NOTE: make sure to register new instance with <code>ObjectMapper</code>
-     * if directly calling this method.
-     */
-    public abstract T with(PropertyNamingStrategy strategy);
-    
-    /**
-     * Method for constructing and returning a new instance with different
-     * root name to use (none, if null).
-     *<p>
-     * Note that when a root name is set to a non-Empty String, this will automatically force use
-     * of root element wrapping with given name. If empty String passed, will
-     * disable root name wrapping; and if null used, will instead use
-     * <code>SerializationFeature</code> to determine if to use wrapping, and annotation
-     * (or default name) for actual root name to use.
-     * 
-     * @param rootName to use: if null, means "use default" (clear setting);
-     *   if empty String ("") means that no root name wrapping is used;
-     *   otherwise defines root name to use.
-     *   
-     * @since 2.6
-     */
-    public abstract T withRootName(PropertyName rootName);
-
-    public T withRootName(String rootName) {
-        if (rootName == null) {
-            return withRootName((PropertyName) null);
-        }
-        return withRootName(PropertyName.construct(rootName));
+    public final T with(ClassIntrospector ci) {
+        return _withBase(_base.withClassIntrospector(ci));
     }
-    
-    /**
-     * Method for constructing and returning a new instance with different
-     * {@link SubtypeResolver}
-     * to use.
-     *<p>
-     * NOTE: make sure to register new instance with <code>ObjectMapper</code>
-     * if directly calling this method.
-     */
-    public abstract T with(SubtypeResolver str);
-    
-    /**
-     * Method for constructing and returning a new instance with different
-     * {@link TypeFactory}
-     * to use.
-     */
-    public abstract T with(TypeFactory typeFactory);
 
-    /**
-     * Method for constructing and returning a new instance with different
-     * {@link TypeResolverBuilder} to use.
+    /*
+    /**********************************************************
+    /* Additional shared fluent factory methods; attributes
+    /**********************************************************
      */
-    public abstract T with(TypeResolverBuilder<?> trb);
-
-    /**
-     * Method for constructing and returning a new instance with different
-     * view to use.
-     */
-    public abstract T withView(Class<?> view);
-    
-    /**
-     * Method for constructing and returning a new instance with different
-     * {@link VisibilityChecker}
-     * to use.
-     */
-    public abstract T with(VisibilityChecker<?> vc);
-
-    /**
-     * Method for constructing and returning a new instance with different
-     * minimal visibility level for specified property type
-     */
-    public abstract T withVisibility(PropertyAccessor forMethod, JsonAutoDetect.Visibility visibility);
-
-    /**
-     * Method for constructing and returning a new instance with different
-     * default {@link java.util.Locale} to use for formatting.
-     */
-    public abstract T with(Locale l);
-
-    /**
-     * Method for constructing and returning a new instance with different
-     * default {@link java.util.TimeZone} to use for formatting of date values.
-     */
-    public abstract T with(TimeZone tz);
-
-    /**
-     * Method for constructing and returning a new instance with different
-     * default {@link Base64Variant} to use with base64-encoded binary values.
-     */
-    public abstract T with(Base64Variant base64);
 
     /**
      * Method for constructing an instance that has specified
@@ -466,13 +421,142 @@
     public T withoutAttribute(Object key) {
         return with(getAttributes().withoutSharedAttribute(key));
     }
+
+    /*
+    /**********************************************************
+    /* Additional shared fluent factory methods; factories
+    /**********************************************************
+     */
+
+    /**
+     * Method for constructing and returning a new instance with different
+     * {@link TypeFactory}
+     * to use.
+     */
+    public final T with(TypeFactory tf) {
+        return _withBase( _base.withTypeFactory(tf));
+    }
+
+    /**
+     * Method for constructing and returning a new instance with different
+     * {@link TypeResolverBuilder} to use.
+     */
+    public final T with(TypeResolverBuilder<?> trb) {
+        return _withBase(_base.withTypeResolverBuilder(trb));
+    }
+
+    /**
+     * Method for constructing and returning a new instance with different
+     * {@link PropertyNamingStrategy}
+     * to use.
+     *<p>
+     * NOTE: make sure to register new instance with <code>ObjectMapper</code>
+     * if directly calling this method.
+     */
+    public final T with(PropertyNamingStrategy pns) {
+        return _withBase(_base.withPropertyNamingStrategy(pns));
+    }
+
+    /**
+     * Method for constructing and returning a new instance with different
+     * {@link HandlerInstantiator}
+     * to use.
+     *<p>
+     * NOTE: make sure to register new instance with <code>ObjectMapper</code>
+     * if directly calling this method.
+     */
+    public final T with(HandlerInstantiator hi) {
+        return _withBase(_base.withHandlerInstantiator(hi));
+    }
+
+    /*
+    /**********************************************************
+    /* Additional shared fluent factory methods; other
+    /**********************************************************
+     */
+
+    /**
+     * Method for constructing and returning a new instance with different
+     * default {@link Base64Variant} to use with base64-encoded binary values.
+     */
+    public final T with(Base64Variant base64) {
+        return _withBase(_base.with(base64));
+    }
+
+    /**
+     * Method for constructing and returning a new instance with different
+     * {@link DateFormat}
+     * to use.
+     *<p>
+     * NOTE: non-final since <code>SerializationConfig</code> needs to override this
+     */
+    public T with(DateFormat df) {
+        return _withBase(_base.withDateFormat(df));
+    }
+
+    /**
+     * Method for constructing and returning a new instance with different
+     * default {@link java.util.Locale} to use for formatting.
+     */
+    public final T with(Locale l) {
+        return _withBase(_base.with(l));
+    }
+
+    /**
+     * Method for constructing and returning a new instance with different
+     * default {@link java.util.TimeZone} to use for formatting of date values.
+     */
+    public final T with(TimeZone tz) {
+        return _withBase(_base.with(tz));
+    }
+
+    /**
+     * Method for constructing and returning a new instance with different
+     * root name to use (none, if null).
+     *<p>
+     * Note that when a root name is set to a non-Empty String, this will automatically force use
+     * of root element wrapping with given name. If empty String passed, will
+     * disable root name wrapping; and if null used, will instead use
+     * <code>SerializationFeature</code> to determine if to use wrapping, and annotation
+     * (or default name) for actual root name to use.
+     * 
+     * @param rootName to use: if null, means "use default" (clear setting);
+     *   if empty String ("") means that no root name wrapping is used;
+     *   otherwise defines root name to use.
+     *   
+     * @since 2.6
+     */
+    public abstract T withRootName(PropertyName rootName);
+
+    public T withRootName(String rootName) {
+        if (rootName == null) {
+            return withRootName((PropertyName) null);
+        }
+        return withRootName(PropertyName.construct(rootName));
+    }
     
+    /**
+     * Method for constructing and returning a new instance with different
+     * {@link SubtypeResolver}
+     * to use.
+     *<p>
+     * NOTE: make sure to register new instance with <code>ObjectMapper</code>
+     * if directly calling this method.
+     */
+    public abstract T with(SubtypeResolver str);
+
+    /**
+     * Method for constructing and returning a new instance with different
+     * view to use.
+     */
+    public abstract T withView(Class<?> view);
+
     /*
     /**********************************************************
     /* Simple accessors
     /**********************************************************
      */
-    
+
     /**
      * Accessor for object used for finding out all reachable subtypes
      * for supertypes; needed when a logical type name is used instead
@@ -510,16 +594,48 @@
 
     /*
     /**********************************************************
-    /* Property config override access
+    /* Configuration access; default/overrides
     /**********************************************************
      */
-    
+
+    @Override
+    public final ConfigOverride getConfigOverride(Class<?> type) {
+        ConfigOverride override = _configOverrides.findOverride(type);
+        return (override == null) ? EMPTY_OVERRIDE : override;
+    }
+
     @Override
     public final ConfigOverride findConfigOverride(Class<?> type) {
         return _configOverrides.findOverride(type);
     }
 
     @Override
+    public final JsonInclude.Value getDefaultPropertyInclusion() {
+        return _configOverrides.getDefaultInclusion();
+    }
+
+    @Override
+    public final JsonInclude.Value getDefaultPropertyInclusion(Class<?> baseType) {
+        JsonInclude.Value v = getConfigOverride(baseType).getInclude();
+        JsonInclude.Value def = getDefaultPropertyInclusion();
+        if (def == null) {
+            return v;
+        }
+        return def.withOverrides(v);
+    }
+
+    @Override
+    public final JsonInclude.Value getDefaultInclusion(Class<?> baseType,
+            Class<?> propertyType) {
+        JsonInclude.Value v = getConfigOverride(propertyType).getIncludeAsProperty();
+        JsonInclude.Value def = getDefaultPropertyInclusion(baseType);
+        if (def == null) {
+            return v;
+        }
+        return def.withOverrides(v);
+    }
+
+    @Override
     public final JsonFormat.Value getDefaultPropertyFormat(Class<?> type) {
         ConfigOverride overrides = _configOverrides.findOverride(type);
         if (overrides != null) {
@@ -556,6 +672,70 @@
         return JsonIgnoreProperties.Value.merge(base, overrides);
     }
 
+    @Override
+    public final VisibilityChecker<?> getDefaultVisibilityChecker()
+    {
+        VisibilityChecker<?> vchecker = _configOverrides.getDefaultVisibility();
+        // then global overrides (disabling)
+        // 05-Mar-2018, tatu: As per [databind#1947], need to see if any disabled
+        if ((_mapperFeatures & AUTO_DETECT_MASK) != AUTO_DETECT_MASK) {
+            if (!isEnabled(MapperFeature.AUTO_DETECT_FIELDS)) {
+                vchecker = vchecker.withFieldVisibility(Visibility.NONE);
+            }
+            if (!isEnabled(MapperFeature.AUTO_DETECT_GETTERS)) {
+                vchecker = vchecker.withGetterVisibility(Visibility.NONE);
+            }
+            if (!isEnabled(MapperFeature.AUTO_DETECT_IS_GETTERS)) {
+                vchecker = vchecker.withIsGetterVisibility(Visibility.NONE);
+            }
+            if (!isEnabled(MapperFeature.AUTO_DETECT_SETTERS)) {
+                vchecker = vchecker.withSetterVisibility(Visibility.NONE);
+            }
+            if (!isEnabled(MapperFeature.AUTO_DETECT_CREATORS)) {
+                vchecker = vchecker.withCreatorVisibility(Visibility.NONE);
+            }
+        }
+        return vchecker;
+    }
+
+    @Override // since 2.9
+    public final VisibilityChecker<?> getDefaultVisibilityChecker(Class<?> baseType,
+            AnnotatedClass actualClass) {
+        VisibilityChecker<?> vc = getDefaultVisibilityChecker();
+        AnnotationIntrospector intr = getAnnotationIntrospector();
+        if (intr != null) {
+            vc = intr.findAutoDetectVisibility(actualClass, vc);
+        }
+        ConfigOverride overrides = _configOverrides.findOverride(baseType);
+        if (overrides != null) {
+            vc = vc.withOverrides(overrides.getVisibility()); // ok to pass null
+        }
+        return vc;
+    }
+
+    @Override
+    public final JsonSetter.Value getDefaultSetterInfo() {
+        return _configOverrides.getDefaultSetterInfo();
+    }
+
+    @Override
+    public Boolean getDefaultMergeable() {
+        return _configOverrides.getDefaultMergeable();
+    }
+
+    @Override
+    public Boolean getDefaultMergeable(Class<?> baseType) {
+        Boolean b;
+        ConfigOverride cfg = _configOverrides.findOverride(baseType);
+        if (cfg != null) {
+            b = cfg.getMergeable();
+            if (b != null) {
+                return b;
+            }
+        }
+        return _configOverrides.getDefaultMergeable();
+    }
+
     /*
     /**********************************************************
     /* Other config access
diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/MutableConfigOverride.java b/src/main/java/com/fasterxml/jackson/databind/cfg/MutableConfigOverride.java
index 902e18b..b30b99b 100644
--- a/src/main/java/com/fasterxml/jackson/databind/cfg/MutableConfigOverride.java
+++ b/src/main/java/com/fasterxml/jackson/databind/cfg/MutableConfigOverride.java
@@ -1,8 +1,10 @@
 package com.fasterxml.jackson.databind.cfg;
 
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonSetter;
 
 /**
  * Extension of {@link ConfigOverride} that allows changing of
@@ -24,8 +26,8 @@
     protected MutableConfigOverride(MutableConfigOverride src) {
         super(src);
     }
-    
-    protected MutableConfigOverride copy() {
+
+    public MutableConfigOverride copy() {
         return new MutableConfigOverride(this);
     }
 
@@ -33,19 +35,62 @@
         _format = v;
         return this;
     }
-    
+
+    /**
+     * Override inclusion setting for all properties contained in POJOs of the
+     * associated type.
+     *
+     * @param v Inclusion setting to apply contained properties.
+     */
     public MutableConfigOverride setInclude(JsonInclude.Value v) {
         _include = v;
         return this;
     }
 
+    /**
+     * Override inclusion setting for properties of the associated type
+     * regardless of the type of the POJO containing it.
+     *
+     * @param v Inclusion setting to apply for properties of associated type.
+     *
+     * @since 2.9
+     */
+    public MutableConfigOverride setIncludeAsProperty(JsonInclude.Value v) {
+        _includeAsProperty = v;
+        return this;
+    }
+
     public MutableConfigOverride setIgnorals(JsonIgnoreProperties.Value v) {
         _ignorals = v;
         return this;
     }
-    
+
     public MutableConfigOverride setIsIgnoredType(Boolean v) {
         _isIgnoredType = v;
         return this;
     }
+
+    /**
+     * @since 2.9
+     */
+    public MutableConfigOverride setSetterInfo(JsonSetter.Value v) {
+        _setterInfo = v;
+        return this;
+    }
+
+    /**
+     * @since 2.9
+     */
+    public MutableConfigOverride setVisibility(JsonAutoDetect.Value v) {
+        _visibility = v;
+        return this;
+    }
+
+    /**
+     * @since 2.9
+     */
+    public MutableConfigOverride setMergeable(Boolean v) {
+        _mergeable = v;
+        return this;
+    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/SerializerFactoryConfig.java b/src/main/java/com/fasterxml/jackson/databind/cfg/SerializerFactoryConfig.java
index e997a49..8437633 100644
--- a/src/main/java/com/fasterxml/jackson/databind/cfg/SerializerFactoryConfig.java
+++ b/src/main/java/com/fasterxml/jackson/databind/cfg/SerializerFactoryConfig.java
@@ -56,7 +56,7 @@
     public SerializerFactoryConfig withAdditionalSerializers(Serializers additional)
     {
         if (additional == null) {
-            throw new IllegalArgumentException("Can not pass null Serializers");
+            throw new IllegalArgumentException("Cannot pass null Serializers");
         }
         Serializers[] all = ArrayBuilders.insertInListNoDup(_additionalSerializers, additional);
         return new SerializerFactoryConfig(all, _additionalKeySerializers, _modifiers);
@@ -65,7 +65,7 @@
     public SerializerFactoryConfig withAdditionalKeySerializers(Serializers additional)
     {
         if (additional == null) {
-            throw new IllegalArgumentException("Can not pass null Serializers");
+            throw new IllegalArgumentException("Cannot pass null Serializers");
         }
         Serializers[] all = ArrayBuilders.insertInListNoDup(_additionalKeySerializers, additional);
         return new SerializerFactoryConfig(_additionalSerializers, all, _modifiers);
@@ -74,7 +74,7 @@
     public SerializerFactoryConfig withSerializerModifier(BeanSerializerModifier modifier)
     {
         if (modifier == null) {
-            throw new IllegalArgumentException("Can not pass null modifier");
+            throw new IllegalArgumentException("Cannot pass null modifier");
         }
         BeanSerializerModifier[] modifiers = ArrayBuilders.insertInListNoDup(_modifiers, modifier);
         return new SerializerFactoryConfig(_additionalSerializers, _additionalKeySerializers, modifiers);
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/AbstractDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/AbstractDeserializer.java
index 8515376..1e1d80e 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/AbstractDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/AbstractDeserializer.java
@@ -11,6 +11,7 @@
 
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.deser.impl.ObjectIdReader;
+import com.fasterxml.jackson.databind.deser.impl.PropertyBasedObjectIdGenerator;
 import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId;
 import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
 import com.fasterxml.jackson.databind.introspect.ObjectIdInfo;
@@ -35,6 +36,8 @@
 
     protected final Map<String, SettableBeanProperty> _backRefProperties;
 
+    protected transient Map<String,SettableBeanProperty> _properties;
+
     // support for "native" types, which require special care:
     
     protected final boolean _acceptString;
@@ -48,12 +51,21 @@
     /**********************************************************
      */
 
+    /**
+     * @since 2.9
+     *
+     * @param props Regular properties: currently only needed to support property-annotated
+     *    Object Id handling with property inclusion (needed for determining type of Object Id
+     *    to bind)
+     */
     public AbstractDeserializer(BeanDeserializerBuilder builder,
-            BeanDescription beanDesc, Map<String, SettableBeanProperty> backRefProps)
+            BeanDescription beanDesc, Map<String, SettableBeanProperty> backRefProps,
+            Map<String, SettableBeanProperty> props)
     {
         _baseType = beanDesc.getType();
         _objectIdReader = builder.getObjectIdReader();
         _backRefProperties = backRefProps;
+        _properties = props;
         Class<?> cls = _baseType.getRawClass();
         _acceptString = cls.isAssignableFrom(String.class);
         _acceptBoolean = (cls == Boolean.TYPE) || cls.isAssignableFrom(Boolean.class);
@@ -61,6 +73,12 @@
         _acceptDouble = (cls == Double.TYPE) || cls.isAssignableFrom(Double.class);
     }
 
+    @Deprecated // since 2.9
+    public AbstractDeserializer(BeanDeserializerBuilder builder,
+            BeanDescription beanDesc, Map<String, SettableBeanProperty> backRefProps) {
+        this(builder, beanDesc, backRefProps, null);
+    }
+
     protected AbstractDeserializer(BeanDescription beanDesc)
     {
         _baseType = beanDesc.getType();
@@ -77,7 +95,7 @@
      * @since 2.9
      */
     protected AbstractDeserializer(AbstractDeserializer base,
-            ObjectIdReader objectIdReader)
+            ObjectIdReader objectIdReader, Map<String, SettableBeanProperty> props)
     {
         _baseType = base._baseType;
         _backRefProperties = base._backRefProperties;
@@ -87,8 +105,9 @@
         _acceptDouble = base._acceptDouble;
 
         _objectIdReader = objectIdReader;
+        _properties = props;
     }
-    
+
     /**
      * Factory method used when constructing instances for non-POJO types, like
      * {@link java.util.Map}s.
@@ -103,37 +122,55 @@
     public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
             BeanProperty property) throws JsonMappingException
     {
-        // First: may have an override for Object Id:
         final AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
-        final AnnotatedMember accessor = (property == null || intr == null)
-                ? null : property.getMember();
-        if (accessor != null && intr != null) {
-            ObjectIdInfo objectIdInfo = intr.findObjectIdInfo(accessor);
-            if (objectIdInfo != null) { // some code duplication here as well (from BeanDeserializerFactory)
-                // 2.1: allow modifications by "id ref" annotations as well:
-                objectIdInfo = intr.findObjectReferenceInfo(accessor, objectIdInfo);
-                
-                Class<?> implClass = objectIdInfo.getGeneratorType();
-                // 02-May-2017, tatu: Alas, properties are NOT available for abstract classes; can not
-                //    support this particular type
-                if (implClass == ObjectIdGenerators.PropertyGenerator.class) {
-                    ctxt.reportMappingException(
-"Invalid Object Id definition for abstract type %s: can not use `PropertyGenerator` on polymorphic types using property annotation",
-handledType().getName());
+        if (property != null && intr != null) {
+            final AnnotatedMember accessor = property.getMember();
+            if (accessor != null) {
+                ObjectIdInfo objectIdInfo = intr.findObjectIdInfo(accessor);
+                if (objectIdInfo != null) { // some code duplication here as well (from BeanDeserializerFactory)
+                    JavaType idType;
+                    ObjectIdGenerator<?> idGen;
+                    SettableBeanProperty idProp = null;
+                    ObjectIdResolver resolver = ctxt.objectIdResolverInstance(accessor, objectIdInfo);
+
+                    // 2.1: allow modifications by "id ref" annotations as well:
+                    objectIdInfo = intr.findObjectReferenceInfo(accessor, objectIdInfo);
+                    Class<?> implClass = objectIdInfo.getGeneratorType();
+
+                    if (implClass == ObjectIdGenerators.PropertyGenerator.class) {
+                        PropertyName propName = objectIdInfo.getPropertyName();
+                        idProp = (_properties == null) ? null : _properties.get(propName.getSimpleName());
+                        if (idProp == null) {
+                            ctxt.reportBadDefinition(_baseType, String.format(
+                                    "Invalid Object Id definition for %s: cannot find property with name '%s'",
+                                    handledType().getName(), propName));
+                        }
+                        idType = idProp.getType();
+                        idGen = new PropertyBasedObjectIdGenerator(objectIdInfo.getScope());
+/*
+                         ctxt.reportBadDefinition(_baseType, String.format(
+/
+"Invalid Object Id definition for abstract type %s: cannot use `PropertyGenerator` on polymorphic types using property annotation",
+handledType().getName()));
+*/
+                    } else { // other types simpler
+                        resolver = ctxt.objectIdResolverInstance(accessor, objectIdInfo);
+                        JavaType type = ctxt.constructType(implClass);
+                        idType = ctxt.getTypeFactory().findTypeParameters(type, ObjectIdGenerator.class)[0];
+                        idGen = ctxt.objectIdGeneratorInstance(accessor, objectIdInfo);
+                    }
+                    JsonDeserializer<?> deser = ctxt.findRootValueDeserializer(idType);
+                    ObjectIdReader oir = ObjectIdReader.construct(idType, objectIdInfo.getPropertyName(),
+                             idGen, deser, idProp, resolver);
+                    return new AbstractDeserializer(this, oir, null);
                 }
-                ObjectIdResolver resolver = ctxt.objectIdResolverInstance(accessor, objectIdInfo);
-                JavaType type = ctxt.constructType(implClass);
-                JavaType idType = ctxt.getTypeFactory().findTypeParameters(type, ObjectIdGenerator.class)[0];
-                SettableBeanProperty idProp = null;
-                ObjectIdGenerator<?> idGen = ctxt.objectIdGeneratorInstance(accessor, objectIdInfo);
-                JsonDeserializer<?> deser = ctxt.findRootValueDeserializer(idType);
-                ObjectIdReader oir = ObjectIdReader.construct(idType, objectIdInfo.getPropertyName(),
-                         idGen, deser, idProp, resolver);
-                return new AbstractDeserializer(this, oir);
             }
         }
-        // either way, need to resolve serializer:
-        return this;
+        if (_properties == null) {
+            return this;
+        }
+        // Need to ensure properties are dropped at this point, regardless
+        return new AbstractDeserializer(this, _objectIdReader, null);
     }
 
     /*
@@ -149,7 +186,17 @@
     
     @Override
     public boolean isCachable() { return true; }
-    
+
+    @Override // since 2.9
+    public Boolean supportsUpdate(DeserializationConfig config) {
+        /* 23-Oct-2016, tatu: Not exactly sure what to do with this; polymorphic
+         *   type handling seems bit risky so for now claim it "may or may not be"
+         *   possible, which does allow explicit per-type/per-property merging attempts,
+         *   but avoids general-configuration merges
+         */
+        return null;
+    }
+
     /**
      * Overridden to return true for those instances that are
      * handling value for which Object Identity handling is enabled
@@ -197,10 +244,8 @@
                         && _objectIdReader.isValidReferencePropertyName(p.getCurrentName(), p)) {
                     return _deserializeFromObjectId(p, ctxt);
                 }
-            
             }
         }
-        
         // First: support "natural" values (which are always serialized without type info!)
         Object result = _deserializeIfNatural(p, ctxt);
         if (result != null) {
@@ -213,7 +258,11 @@
     public Object deserialize(JsonParser p, DeserializationContext ctxt)
         throws IOException
     {
-        return ctxt.handleMissingInstantiator(_baseType.getRawClass(), p,
+        // 16-Oct-2016, tatu: Let's pass non-null value instantiator so that we will
+        //    get proper exception type; needed to establish there are no creators
+        //    (since without ValueInstantiator this would not be known for certain)
+        ValueInstantiator bogus = new ValueInstantiator.Base(_baseType);
+        return ctxt.handleMissingInstantiator(_baseType.getRawClass(), bogus, p,
                 "abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information");
     }
 
@@ -222,7 +271,7 @@
     /* Internal methods
     /**********************************************************
      */
-    
+
     protected Object _deserializeIfNatural(JsonParser p, DeserializationContext ctxt) throws IOException
     {
         /* There is a chance we might be "natural" types
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java b/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java
index 3dd3c4b..042cf0a 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java
@@ -1,18 +1,23 @@
 package com.fasterxml.jackson.databind.deser;
 
-import java.lang.reflect.Method;
 import java.util.*;
 import java.util.concurrent.*;
 import java.util.concurrent.atomic.AtomicReference;
 
+import com.fasterxml.jackson.annotation.JacksonInject;
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonCreator.Mode;
 import com.fasterxml.jackson.core.JsonLocation;
+import com.fasterxml.jackson.core.JsonParser;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.cfg.DeserializerFactoryConfig;
 import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
+import com.fasterxml.jackson.databind.deser.impl.CreatorCandidate;
 import com.fasterxml.jackson.databind.deser.impl.CreatorCollector;
+import com.fasterxml.jackson.databind.deser.impl.JavaUtilCollectionsDeserializers;
 import com.fasterxml.jackson.databind.deser.std.*;
+import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
 import com.fasterxml.jackson.databind.ext.OptionalHandlerFactory;
 import com.fasterxml.jackson.databind.introspect.*;
 import com.fasterxml.jackson.databind.jsontype.NamedType;
@@ -38,7 +43,7 @@
 {
     private final static Class<?> CLASS_OBJECT = Object.class;
     private final static Class<?> CLASS_STRING = String.class;
-    private final static Class<?> CLASS_CHAR_BUFFER = CharSequence.class;
+    private final static Class<?> CLASS_CHAR_SEQUENCE = CharSequence.class;
     private final static Class<?> CLASS_ITERABLE = Iterable.class;
     private final static Class<?> CLASS_MAP_ENTRY = Map.Entry.class;
 
@@ -211,14 +216,14 @@
         if (_factoryConfig.hasAbstractTypeResolvers()) {
             for (AbstractTypeResolver resolver : _factoryConfig.abstractTypeResolvers()) {
                 JavaType concrete = resolver.findTypeMapping(config, type);
-                if (concrete != null && concrete.getRawClass() != currClass) {
+                if ((concrete != null) && !concrete.hasRawClass(currClass)) {
                     return concrete;
                 }
             }
         }
         return null;
     }
-    
+
     /*
     /**********************************************************
     /* JsonDeserializerFactory impl (partial): ValueInstantiators
@@ -238,29 +243,28 @@
         final DeserializationConfig config = ctxt.getConfig();
 
         ValueInstantiator instantiator = null;
-        // [JACKSON-633] Check @JsonValueInstantiator before anything else
+        // Check @JsonValueInstantiator before anything else
         AnnotatedClass ac = beanDesc.getClassInfo();
         Object instDef = ctxt.getAnnotationIntrospector().findValueInstantiator(ac);
         if (instDef != null) {
             instantiator = _valueInstantiatorInstance(config, ac, instDef);
         }
         if (instantiator == null) {
-            /* Second: see if some of standard Jackson/JDK types might provide value
-             * instantiators.
-             */
+            // Second: see if some of standard Jackson/JDK types might provide value
+            // instantiators.
             instantiator = _findStdValueInstantiator(config, beanDesc);
             if (instantiator == null) {
                 instantiator = _constructDefaultValueInstantiator(ctxt, beanDesc);
             }
         }
-        
+
         // finally: anyone want to modify ValueInstantiator?
         if (_factoryConfig.hasValueInstantiators()) {
             for (ValueInstantiators insts : _factoryConfig.valueInstantiators()) {
                 instantiator = insts.findValueInstantiator(config, beanDesc, instantiator);
                 // let's do sanity check; easier to spot buggy handlers
                 if (instantiator == null) {
-		    ctxt.reportMappingException(
+                    ctxt.reportBadTypeDefinition(beanDesc,
 						"Broken registered ValueInstantiators (of type %s): returned null ValueInstantiator",
 						insts.getClass().getName());
                 }
@@ -271,7 +275,8 @@
         if (instantiator.getIncompleteParameter() != null) {
             final AnnotatedParameter nonAnnotatedParam = instantiator.getIncompleteParameter();
             final AnnotatedWithParams ctor = nonAnnotatedParam.getOwner();
-            throw new IllegalArgumentException("Argument #"+nonAnnotatedParam.getIndex()+" of constructor "+ctor+" has no property name annotation; must have name when multiple-parameter constructor annotated as Creator");
+            throw new IllegalArgumentException("Argument #"+nonAnnotatedParam.getIndex()
+                +" of constructor "+ctor+" has no property name annotation; must have name when multiple-parameter constructor annotated as Creator");
         }
 
         return instantiator;
@@ -285,6 +290,19 @@
         if (raw == JsonLocation.class) {
             return new JsonLocationInstantiator();
         }
+        // [databind#1868]: empty List/Set/Map
+        if (Collection.class.isAssignableFrom(raw)) {
+            if (Collections.EMPTY_SET.getClass() == raw) {
+                return new ConstantValueInstantiator(Collections.EMPTY_SET);
+            }
+            if (Collections.EMPTY_LIST.getClass() == raw) {
+                return new ConstantValueInstantiator(Collections.EMPTY_LIST);
+            }
+        } else if (Map.class.isAssignableFrom(raw)) {
+            if (Collections.EMPTY_MAP.getClass() == raw) {
+                return new ConstantValueInstantiator(Collections.EMPTY_MAP);
+            }
+        }
         return null;
     }
 
@@ -301,8 +319,8 @@
         
         // need to construct suitable visibility checker:
         final DeserializationConfig config = ctxt.getConfig();
-        VisibilityChecker<?> vchecker = config.getDefaultVisibilityChecker();
-        vchecker = intr.findAutoDetectVisibility(beanDesc.getClassInfo(), vchecker);
+        VisibilityChecker<?> vchecker = config.getDefaultVisibilityChecker(beanDesc.getBeanClass(),
+                beanDesc.getClassInfo());
 
         /* 24-Sep-2014, tatu: Tricky part first; need to merge resolved property information
          *  (which has creator parameters sprinkled around) with actual creator
@@ -321,7 +339,7 @@
         if (beanDesc.getType().isConcrete()) {
             _addDeserializerConstructors(ctxt, beanDesc, vchecker, intr, creators, creatorDefs);
         }
-        return creators.constructValueInstantiator(config);
+        return creators.constructValueInstantiator(ctxt);
     }
 
     protected Map<AnnotatedWithParams,BeanPropertyDefinition[]> _findCreatorsFromProperties(DeserializationContext ctxt,
@@ -344,8 +362,9 @@
                     result.put(owner, defs);
                 } else {
                     if (defs[index] != null) {
-                        throw new IllegalStateException("Conflict: parameter #"+index+" of "+owner
-                                +" bound to more than one property; "+defs[index]+" vs "+propDef);
+                        ctxt.reportBadTypeDefinition(beanDesc,
+"Conflict: parameter #%d of %s bound to more than one property; %s vs %s",
+index, owner, defs[index], propDef);
                     }
                 }
                 defs[index] = propDef;
@@ -391,22 +410,18 @@
                 config.canOverrideAccessModifiers());
     }
 
-    protected void _addDeserializerConstructors
-        (DeserializationContext ctxt, BeanDescription beanDesc, VisibilityChecker<?> vchecker,
+    /*
+    /**********************************************************
+    /* Creator introspection
+    /**********************************************************
+     */
+
+    protected void _addDeserializerConstructors(DeserializationContext ctxt,
+            BeanDescription beanDesc, VisibilityChecker<?> vchecker,
          AnnotationIntrospector intr, CreatorCollector creators,
          Map<AnnotatedWithParams,BeanPropertyDefinition[]> creatorParams)
-        throws JsonMappingException
+                 throws JsonMappingException
     {
-        // First things first: the "default constructor" (zero-arg
-        // constructor; whether implicit or explicit) is NOT included
-        // in list of constructors, so needs to be handled separately.
-        AnnotatedConstructor defaultCtor = beanDesc.findDefaultConstructor();
-        if (defaultCtor != null) {
-            if (!creators.hasDefaultCreator() || intr.hasCreatorAnnotation(defaultCtor)) {
-                creators.setDefaultCreator(defaultCtor);
-            }
-        }
-
         // 25-Jan-2017, tatu: As per [databind#1501], [databind#1502], [databind#1503], best
         //     for now to skip attempts at using anything but no-args constructor (see
         //     `InnerClassProperty` construction for that)
@@ -416,33 +431,74 @@
             return;
         }
 
-        // may need to keep track for [#725]
-        List<AnnotatedConstructor> implicitCtors = null;
+        // First things first: the "default constructor" (zero-arg
+        // constructor; whether implicit or explicit) is NOT included
+        // in list of constructors, so needs to be handled separately.
+        AnnotatedConstructor defaultCtor = beanDesc.findDefaultConstructor();
+        if (defaultCtor != null) {
+            if (!creators.hasDefaultCreator() || _hasCreatorAnnotation(ctxt, defaultCtor)) {
+                creators.setDefaultCreator(defaultCtor);
+            }
+        }
+        // 21-Sep-2017, tatu: First let's handle explicitly annotated ones
+        List<CreatorCandidate> nonAnnotated = new LinkedList<>();
+        int explCount = 0;
         for (AnnotatedConstructor ctor : beanDesc.getConstructors()) {
-            final boolean isCreator = intr.hasCreatorAnnotation(ctor);
-            BeanPropertyDefinition[] propDefs = creatorParams.get(ctor);
-            final int argCount = ctor.getParameterCount();
+            JsonCreator.Mode creatorMode = intr.findCreatorAnnotation(ctxt.getConfig(), ctor);
+            if (Mode.DISABLED == creatorMode) {
+                continue;
+            }
+            if (creatorMode == null) {
+                // let's check Visibility here, to avoid further processing for non-visible?
+                if (vchecker.isCreatorVisible(ctor)) {
+                    nonAnnotated.add(CreatorCandidate.construct(intr, ctor, creatorParams.get(ctor)));
+                }
+                continue;
+            }
+            switch (creatorMode) {
+            case DELEGATING:
+                _addExplicitDelegatingCreator(ctxt, beanDesc, creators,
+                        CreatorCandidate.construct(intr, ctor, null));
+                break;
+            case PROPERTIES:
+                _addExplicitPropertyCreator(ctxt, beanDesc, creators,
+                        CreatorCandidate.construct(intr, ctor, creatorParams.get(ctor)));
+                break;
+            default:
+                _addExplicitAnyCreator(ctxt, beanDesc, creators,
+                        CreatorCandidate.construct(intr, ctor, creatorParams.get(ctor)));
+                break;
+            }
+            ++explCount;
+        }
+        // And only if and when those handled, consider potentially visible ones
+        if (explCount > 0) { // TODO: split method into two since we could have expl factories
+            return;
+        }
+        List<AnnotatedWithParams> implicitCtors = null;
+        for (CreatorCandidate candidate : nonAnnotated) {
+            final int argCount = candidate.paramCount();
+            final AnnotatedWithParams ctor = candidate.creator();
 
             // some single-arg factory methods (String, number) are auto-detected
             if (argCount == 1) {
-                BeanPropertyDefinition argDef = (propDefs == null) ? null : propDefs[0];
-                boolean useProps = _checkIfCreatorPropertyBased(intr, ctor, argDef);
+                BeanPropertyDefinition propDef = candidate.propertyDef(0);
+                boolean useProps = _checkIfCreatorPropertyBased(intr, ctor, propDef);
 
                 if (useProps) {
                     SettableBeanProperty[] properties = new SettableBeanProperty[1];
-                    PropertyName name = (argDef == null) ? null : argDef.getFullName();
-                    AnnotatedParameter arg = ctor.getParameter(0);
-                    properties[0] = constructCreatorProperty(ctxt, beanDesc, name, 0, arg,
-                            intr.findInjectableValueId(arg));
-                    creators.addPropertyCreator(ctor, isCreator, properties);
+                    PropertyName name = candidate.paramName(0);
+                    properties[0] = constructCreatorProperty(ctxt, beanDesc, name, 0,
+                            candidate.parameter(0), candidate.injection(0));
+                    creators.addPropertyCreator(ctor, false, properties);
                 } else {
-                    /*boolean added = */ _handleSingleArgumentConstructor(ctxt, beanDesc, vchecker, intr, creators,
-                            ctor, isCreator,
+                    /*boolean added = */ _handleSingleArgumentCreator(creators,
+                            ctor, false,
                             vchecker.isCreatorVisible(ctor));
                     // one more thing: sever link to creator property, to avoid possible later
                     // problems with "unresolved" constructor property
-                    if (argDef != null) {
-                        ((POJOPropertyBuilder) argDef).removeConstructors();
+                    if (propDef != null) {
+                        ((POJOPropertyBuilder) propDef).removeConstructors();
                     }
                 }
                 // regardless, fully handled
@@ -453,7 +509,7 @@
             // 14-Mar-2015, tatu (2.6): Or, as per [#725], implicit names will also
             //   do, with some constraints. But that will require bit post processing...
 
-            AnnotatedParameter nonAnnotatedParam = null;
+            int nonAnnotatedParamIndex = -1;
             SettableBeanProperty[] properties = new SettableBeanProperty[argCount];
             int explicitNameCount = 0;
             int implicitWithCreatorCount = 0;
@@ -461,8 +517,8 @@
 
             for (int i = 0; i < argCount; ++i) {
                 final AnnotatedParameter param = ctor.getParameter(i);
-                BeanPropertyDefinition propDef = (propDefs == null) ? null : propDefs[i];
-                Object injectId = intr.findInjectableValueId(param);
+                BeanPropertyDefinition propDef = candidate.propertyDef(i);
+                JacksonInject.Value injectId = intr.findInjectableValue(param);
                 final PropertyName name = (propDef == null) ? null : propDef.getFullName();
 
                 if (propDef != null && propDef.isExplicitlyNamed()) {
@@ -477,56 +533,61 @@
                 }
                 NameTransformer unwrapper = intr.findUnwrappingNameTransformer(param);
                 if (unwrapper != null) {
+                    _reportUnwrappedCreatorProperty(ctxt, beanDesc, param);
+                    /*
                     properties[i] = constructCreatorProperty(ctxt, beanDesc, UNWRAPPED_CREATOR_PARAM_NAME, i, param, null);
                     ++explicitNameCount;
+                    */
                     continue;
                 }
                 // One more thing: implicit names are ok iff ctor has creator annotation
+                /*
                 if (isCreator && (name != null && !name.isEmpty())) {
                     ++implicitWithCreatorCount;
                     properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectId);
                     continue;
                 }
-                if (nonAnnotatedParam == null) {
-                    nonAnnotatedParam = param;
+                */
+                if (nonAnnotatedParamIndex < 0) {
+                    nonAnnotatedParamIndex = i;
                 }
             }
 
             final int namedCount = explicitNameCount + implicitWithCreatorCount;
             // Ok: if named or injectable, we have more work to do
-            if (isCreator || (explicitNameCount > 0) || (injectCount > 0)) {
+            if ((explicitNameCount > 0) || (injectCount > 0)) {
                 // simple case; everything covered:
                 if ((namedCount + injectCount) == argCount) {
-                    creators.addPropertyCreator(ctor, isCreator, properties);
+                    creators.addPropertyCreator(ctor, false, properties);
                     continue;
                 }
                 if ((explicitNameCount == 0) && ((injectCount + 1) == argCount)) {
                     // Secondary: all but one injectable, one un-annotated (un-named)
-                    creators.addDelegatingCreator(ctor, isCreator, properties);
+                    creators.addDelegatingCreator(ctor, false, properties, 0);
                     continue;
                 }
                 // otherwise, epic fail?
                 // 16-Mar-2015, tatu: due to [#725], need to be more permissive. For now let's
                 //    only report problem if there's no implicit name
-                PropertyName impl = _findImplicitParamName(nonAnnotatedParam, intr);
+                PropertyName impl = candidate.findImplicitParamName(nonAnnotatedParamIndex);
                 if (impl == null || impl.isEmpty()) {
                     // Let's consider non-static inner class as a special case...
-                    int ix = nonAnnotatedParam.getIndex();
                     // 25-Jan-2017, tatu: Non-static inner classes skipped altogether, now
                     /*
-                    if ((ix == 0) && isNonStaticInnerClass) {
+                    if ((nonAnnotatedParamIndex == 0) && isNonStaticInnerClass) {
                         throw new IllegalArgumentException("Non-static inner classes like "
-                                +ctor.getDeclaringClass().getName()+" can not use @JsonCreator for constructors");
+                                +ctor.getDeclaringClass().getName()+" cannot use @JsonCreator for constructors");
                     }
                     */
-                    throw new IllegalArgumentException("Argument #"+ix
-                            +" of constructor "+ctor+" has no property name annotation; must have name when multiple-parameter constructor annotated as Creator");
+                    ctxt.reportBadTypeDefinition(beanDesc,
+"Argument #%d of constructor %s has no property name annotation; must have name when multiple-parameter constructor annotated as Creator",
+nonAnnotatedParamIndex, ctor);
                 }
             }
             // [#725]: as a fallback, all-implicit names may work as well
             if (!creators.hasDefaultCreator()) {
                 if (implicitCtors == null) {
-                    implicitCtors = new LinkedList<AnnotatedConstructor>();
+                    implicitCtors = new LinkedList<>();
                 }
                 implicitCtors.add(ctor);
             }
@@ -540,12 +601,184 @@
         }
     }
 
-    protected void _checkImplicitlyNamedConstructors(DeserializationContext ctxt,
+    /**
+     * Helper method called when there is the explicit "is-creator" with mode of "delegating"
+     *
+     * @since 2.9.2
+     */
+    protected void _addExplicitDelegatingCreator(DeserializationContext ctxt,
+            BeanDescription beanDesc, CreatorCollector creators,
+            CreatorCandidate candidate)
+        throws JsonMappingException
+    {
+        // Somewhat simple: find injectable values, if any, ensure there is one
+        // and just one delegated argument; report violations if any
+
+        int ix = -1;
+        final int argCount = candidate.paramCount();
+        SettableBeanProperty[] properties = new SettableBeanProperty[argCount];
+        for (int i = 0; i < argCount; ++i) {
+            AnnotatedParameter param = candidate.parameter(i);
+            JacksonInject.Value injectId = candidate.injection(i);
+            if (injectId != null) {
+                properties[i] = constructCreatorProperty(ctxt, beanDesc, null, i, param, injectId);
+                continue;
+            }
+            if (ix < 0) {
+                ix = i;
+                continue;
+            }
+            // Illegal to have more than one value to delegate to
+            ctxt.reportBadTypeDefinition(beanDesc,
+                    "More than one argument (#%d and #%d) left as delegating for Creator %s: only one allowed",
+                    ix, i, candidate);
+        }
+        // Also, let's require that one Delegating argument does eixt
+        if (ix < 0) {
+            ctxt.reportBadTypeDefinition(beanDesc,
+                    "No argument left as delegating for Creator %s: exactly one required", candidate);
+        }
+        // 17-Jan-2018, tatu: as per [databind#1853] need to ensure we will distinguish
+        //   "well-known" single-arg variants (String, int/long, boolean) from "generic" delegating...
+        if (argCount == 1) {
+            _handleSingleArgumentCreator(creators, candidate.creator(), true, true);
+            // one more thing: sever link to creator property, to avoid possible later
+            // problems with "unresolved" constructor property
+            BeanPropertyDefinition paramDef = candidate.propertyDef(0);
+            if (paramDef != null) {
+                ((POJOPropertyBuilder) paramDef).removeConstructors();
+            }
+            return;
+        }
+        creators.addDelegatingCreator(candidate.creator(), true, properties, ix);
+    }
+
+    /**
+     * Helper method called when there is the explicit "is-creator" with mode of "properties-based"
+     *
+     * @since 2.9.2
+     */
+    protected void _addExplicitPropertyCreator(DeserializationContext ctxt,
+            BeanDescription beanDesc, CreatorCollector creators,
+            CreatorCandidate candidate)
+        throws JsonMappingException
+    {
+        final int paramCount = candidate.paramCount();
+        SettableBeanProperty[] properties = new SettableBeanProperty[paramCount];
+
+        for (int i = 0; i < paramCount; ++i) {
+            JacksonInject.Value injectId = candidate.injection(i);
+            AnnotatedParameter param = candidate.parameter(i);
+            PropertyName name = candidate.paramName(i);
+            if (name == null) {
+                // 21-Sep-2017, tatu: Looks like we want to block accidental use of Unwrapped,
+                //   as that will not work with Creators well at all
+                NameTransformer unwrapper = ctxt.getAnnotationIntrospector().findUnwrappingNameTransformer(param);
+                if (unwrapper != null) {
+                    _reportUnwrappedCreatorProperty(ctxt, beanDesc, param);
+                    /*
+                    properties[i] = constructCreatorProperty(ctxt, beanDesc, UNWRAPPED_CREATOR_PARAM_NAME, i, param, null);
+                    ++explicitNameCount;
+                    */
+                }
+                name = candidate.findImplicitParamName(i);
+                // Must be injectable or have name; without either won't work
+                if ((name == null) && (injectId == null)) {
+                    ctxt.reportBadTypeDefinition(beanDesc,
+"Argument #%d has no property name, is not Injectable: can not use as Creator %s", i, candidate);
+                }
+            }
+            properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectId);
+        }
+        creators.addPropertyCreator(candidate.creator(), true, properties);
+    }
+
+    /**
+     * Helper method called when there is the explicit "is-creator", but no mode declaration.
+     *
+     * @since 2.9.2
+     */
+    protected void _addExplicitAnyCreator(DeserializationContext ctxt,
+            BeanDescription beanDesc, CreatorCollector creators,
+            CreatorCandidate candidate)
+        throws JsonMappingException
+    {
+        // Looks like there's bit of magic regarding 1-parameter creators; others simpler:
+        if (1 != candidate.paramCount()) {
+            // Ok: for delegates, we want one and exactly one parameter without
+            // injection AND without name
+            int oneNotInjected = candidate.findOnlyParamWithoutInjection();
+            if (oneNotInjected >= 0) {
+                // getting close; but most not have name
+                if (candidate.paramName(oneNotInjected) == null) {
+                    _addExplicitDelegatingCreator(ctxt, beanDesc, creators, candidate);
+                    return;
+                }
+            }
+            _addExplicitPropertyCreator(ctxt, beanDesc, creators, candidate);
+            return;
+        }
+        AnnotatedParameter param = candidate.parameter(0);
+        JacksonInject.Value injectId = candidate.injection(0);
+        PropertyName paramName = candidate.explicitParamName(0);
+        BeanPropertyDefinition paramDef = candidate.propertyDef(0);
+
+        // If there's injection or explicit name, should be properties-based
+        boolean useProps = (paramName != null) || (injectId != null);
+        if (!useProps && (paramDef != null)) {
+            // One more thing: if implicit name matches property with a getter
+            // or field, we'll consider it property-based as well
+
+            // 25-May-2018, tatu: as per [databind#2051], looks like we have to get
+            //    not implicit name, but name with possible strategy-based-rename
+//            paramName = candidate.findImplicitParamName(0);
+            paramName = candidate.paramName(0);
+            useProps = (paramName != null) && paramDef.couldSerialize();
+        }
+        if (useProps) {
+            SettableBeanProperty[] properties = new SettableBeanProperty[] {
+                    constructCreatorProperty(ctxt, beanDesc, paramName, 0, param, injectId)
+            };
+            creators.addPropertyCreator(candidate.creator(), true, properties);
+            return;
+        }
+        _handleSingleArgumentCreator(creators, candidate.creator(), true, true);
+
+        // one more thing: sever link to creator property, to avoid possible later
+        // problems with "unresolved" constructor property
+        if (paramDef != null) {
+            ((POJOPropertyBuilder) paramDef).removeConstructors();
+        }
+    }
+
+    private boolean _checkIfCreatorPropertyBased(AnnotationIntrospector intr,
+            AnnotatedWithParams creator, BeanPropertyDefinition propDef)
+    {
+        // If explicit name, or inject id, property-based
+        if (((propDef != null) && propDef.isExplicitlyNamed())
+                || (intr.findInjectableValue(creator.getParameter(0)) != null)) {
+            return true;
+        }
+        if (propDef != null) {
+            // One more thing: if implicit name matches property with a getter
+            // or field, we'll consider it property-based as well
+            String implName = propDef.getName();
+            if (implName != null && !implName.isEmpty()) {
+                if (propDef.couldSerialize()) {
+                    return true;
+                }
+            }
+        }
+        // in absence of everything else, default to delegating
+        return false;
+    }
+
+    private void _checkImplicitlyNamedConstructors(DeserializationContext ctxt,
             BeanDescription beanDesc, VisibilityChecker<?> vchecker,
             AnnotationIntrospector intr, CreatorCollector creators,
-            List<AnnotatedConstructor> implicitCtors) throws JsonMappingException
+            List<AnnotatedWithParams> implicitCtors) throws JsonMappingException
     {
-        AnnotatedConstructor found = null;
+        AnnotatedWithParams found = null;
         SettableBeanProperty[] foundProps = null;
 
         // Further checks: (a) must have names for all parameters, (b) only one visible
@@ -553,7 +786,7 @@
         // `@JsonCreator` (or equivalent) annotation, we need to do bit more re-inspection...
 
         main_loop:
-        for (AnnotatedConstructor ctor : implicitCtors) {
+        for (AnnotatedWithParams ctor : implicitCtors) {
             if (!vchecker.isCreatorVisible(ctor)) {
                 continue;
             }
@@ -571,7 +804,7 @@
                 properties[i] = constructCreatorProperty(ctxt, beanDesc, name, param.getIndex(),
                         param, /*injectId*/ null);
             }
-            if (found != null) { // only one allowed
+            if (found != null) { // only one allowed; but multiple not an error
                 found = null;
                 break;
             }
@@ -594,45 +827,160 @@
         }
     }
 
-    protected boolean _checkIfCreatorPropertyBased(AnnotationIntrospector intr,
-            AnnotatedWithParams creator, BeanPropertyDefinition propDef)
+    protected void _addDeserializerFactoryMethods
+        (DeserializationContext ctxt, BeanDescription beanDesc, VisibilityChecker<?> vchecker,
+         AnnotationIntrospector intr, CreatorCollector creators,
+         Map<AnnotatedWithParams,BeanPropertyDefinition[]> creatorParams)
+        throws JsonMappingException
     {
-        JsonCreator.Mode mode = intr.findCreatorBinding(creator);
+        List<CreatorCandidate> nonAnnotated = new LinkedList<>();
+        int explCount = 0;
 
-        if (mode == JsonCreator.Mode.PROPERTIES) {
-            return true;
+        // 21-Sep-2017, tatu: First let's handle explicitly annotated ones
+        for (AnnotatedMethod factory : beanDesc.getFactoryMethods()) {
+            JsonCreator.Mode creatorMode = intr.findCreatorAnnotation(ctxt.getConfig(), factory);
+            final int argCount = factory.getParameterCount();
+            if (creatorMode == null) {
+                // Only potentially accept 1-argument factory methods
+                if ((argCount == 1) && vchecker.isCreatorVisible(factory)) {
+                    nonAnnotated.add(CreatorCandidate.construct(intr, factory, null));
+                }
+                continue;
+            }
+            if (creatorMode == Mode.DISABLED) {
+                continue;
+            }
+            
+            // zero-arg method factory methods fine, as long as explicit
+            if (argCount == 0) {
+                creators.setDefaultCreator(factory);
+                continue;
+            }
+
+            switch (creatorMode) {
+            case DELEGATING:
+                _addExplicitDelegatingCreator(ctxt, beanDesc, creators,
+                        CreatorCandidate.construct(intr, factory, null));
+                break;
+            case PROPERTIES:
+                _addExplicitPropertyCreator(ctxt, beanDesc, creators,
+                        CreatorCandidate.construct(intr, factory, creatorParams.get(factory)));
+                break;
+            case DEFAULT:
+            default:
+                _addExplicitAnyCreator(ctxt, beanDesc, creators,
+                        CreatorCandidate.construct(intr, factory, creatorParams.get(factory)));
+                break;
+            }
+            ++explCount;
         }
-        if (mode == JsonCreator.Mode.DELEGATING) {
-            return false;
+        // And only if and when those handled, consider potentially visible ones
+        if (explCount > 0) { // TODO: split method into two since we could have expl factories
+            return;
         }
-        // If explicit name, or inject id, property-based
-        if (((propDef != null) && propDef.isExplicitlyNamed())
-                || (intr.findInjectableValueId(creator.getParameter(0)) != null)) {
-            return true;
-        }
-        if (propDef != null) {
-            // One more thing: if implicit name matches property with a getter
-            // or field, we'll consider it property-based as well
-            String implName = propDef.getName();
-            if (implName != null && !implName.isEmpty()) {
-                if (propDef.couldSerialize()) {
-                    return true;
+        // And then implicitly found
+        for (CreatorCandidate candidate : nonAnnotated) {
+            final int argCount = candidate.paramCount();
+            AnnotatedWithParams factory = candidate.creator();
+            final BeanPropertyDefinition[] propDefs = creatorParams.get(factory);
+            // some single-arg factory methods (String, number) are auto-detected
+            if (argCount != 1) {
+                continue; // 2 and more args? Must be explicit, handled earlier
+            }
+            BeanPropertyDefinition argDef = candidate.propertyDef(0);
+            boolean useProps = _checkIfCreatorPropertyBased(intr, factory, argDef);
+            if (!useProps) { // not property based but delegating
+                /*boolean added=*/ _handleSingleArgumentCreator(creators,
+                        factory, false, vchecker.isCreatorVisible(factory));
+                // 23-Sep-2016, tatu: [databind#1383]: Need to also sever link to avoid possible
+                //    later problems with "unresolved" constructor property
+                if (argDef != null) {
+                    ((POJOPropertyBuilder) argDef).removeConstructors();
+                }
+                continue;
+            }
+            AnnotatedParameter nonAnnotatedParam = null;            
+            SettableBeanProperty[] properties = new SettableBeanProperty[argCount];
+            int implicitNameCount = 0;
+            int explicitNameCount = 0;
+            int injectCount = 0;
+            
+            for (int i = 0; i < argCount; ++i) {
+                final AnnotatedParameter param = factory.getParameter(i);
+                BeanPropertyDefinition propDef = (propDefs == null) ? null : propDefs[i];
+                JacksonInject.Value injectable = intr.findInjectableValue(param);
+                final PropertyName name = (propDef == null) ? null : propDef.getFullName();
+
+                if (propDef != null && propDef.isExplicitlyNamed()) {
+                    ++explicitNameCount;
+                    properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectable);
+                    continue;
+                }
+                if (injectable != null) {
+                    ++injectCount;
+                    properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectable);
+                    continue;
+                }
+                NameTransformer unwrapper = intr.findUnwrappingNameTransformer(param);
+                if (unwrapper != null) {
+                    _reportUnwrappedCreatorProperty(ctxt, beanDesc, param);
+                    /*
+                    properties[i] = constructCreatorProperty(ctxt, beanDesc, UNWRAPPED_CREATOR_PARAM_NAME, i, param, null);
+                    ++implicitNameCount;
+                    */
+                    continue;
+                }
+                // One more thing: implicit names are ok iff ctor has creator annotation
+                /*
+                if (isCreator) {
+                    if (name != null && !name.isEmpty()) {
+                        ++implicitNameCount;
+                        properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectable);
+                        continue;
+                    }
+                }
+                */
+                /* 25-Sep-2014, tatu: Actually, we may end up "losing" naming due to higher-priority constructor
+                 *  (see TestCreators#testConstructorCreator() test). And just to avoid running into that problem,
+                 *  let's add one more work around
+                 */
+                /*
+                PropertyName name2 = _findExplicitParamName(param, intr);
+                if (name2 != null && !name2.isEmpty()) {
+                    // Hmmh. Ok, fine. So what are we to do with it... ?
+                    // For now... skip. May need to revisit this, should this become problematic
+                    continue main_loop;
+                }
+                */
+                if (nonAnnotatedParam == null) {
+                    nonAnnotatedParam = param;
+                }
+            }
+            final int namedCount = explicitNameCount + implicitNameCount;
+            
+            // Ok: if named or injectable, we have more work to do
+            if (explicitNameCount > 0 || injectCount > 0) {
+                // simple case; everything covered:
+                if ((namedCount + injectCount) == argCount) {
+                    creators.addPropertyCreator(factory, false, properties);
+                } else if ((explicitNameCount == 0) && ((injectCount + 1) == argCount)) {
+                    // secondary: all but one injectable, one un-annotated (un-named)
+                    creators.addDelegatingCreator(factory, false, properties, 0);
+                } else { // otherwise, epic fail
+                    ctxt.reportBadTypeDefinition(beanDesc,
+"Argument #%d of factory method %s has no property name annotation; must have name when multiple-parameter constructor annotated as Creator",
+                    nonAnnotatedParam.getIndex(), factory);
                 }
             }
         }
-        // in absence of everything else, default to delegating
-        return false;
     }
-    
-    protected boolean _handleSingleArgumentConstructor(DeserializationContext ctxt,
-            BeanDescription beanDesc, VisibilityChecker<?> vchecker,
-            AnnotationIntrospector intr, CreatorCollector creators,
-            AnnotatedConstructor ctor, boolean isCreator, boolean isVisible)
-        throws JsonMappingException
+
+    protected boolean _handleSingleArgumentCreator(CreatorCollector creators,
+            AnnotatedWithParams ctor, boolean isCreator, boolean isVisible)
     {
         // otherwise either 'simple' number, String, or general delegate:
         Class<?> type = ctor.getRawParameterType(0);
-        if (type == String.class || type == CharSequence.class) {
+        if (type == String.class || type == CLASS_CHAR_SEQUENCE) {
             if (isCreator || isVisible) {
                 creators.addStringCreator(ctor, isCreator);
             }
@@ -664,166 +1012,21 @@
         }
         // Delegating Creator ok iff it has @JsonCreator (etc)
         if (isCreator) {
-            creators.addDelegatingCreator(ctor, isCreator, null);
+            creators.addDelegatingCreator(ctor, isCreator, null, 0);
             return true;
         }
         return false;
     }
 
-    protected void _addDeserializerFactoryMethods
-        (DeserializationContext ctxt, BeanDescription beanDesc, VisibilityChecker<?> vchecker,
-         AnnotationIntrospector intr, CreatorCollector creators,
-         Map<AnnotatedWithParams,BeanPropertyDefinition[]> creatorParams)
+    // 01-Dec-2016, tatu: As per [databind#265] we cannot yet support passing
+    //   of unwrapped values through creator properties, so fail fast
+    protected void _reportUnwrappedCreatorProperty(DeserializationContext ctxt,
+            BeanDescription beanDesc, AnnotatedParameter param)
         throws JsonMappingException
     {
-        final DeserializationConfig config = ctxt.getConfig();
-        for (AnnotatedMethod factory : beanDesc.getFactoryMethods()) {
-            final boolean isCreator = intr.hasCreatorAnnotation(factory);
-            final int argCount = factory.getParameterCount();
-            // zero-arg methods must be annotated; if so, are "default creators" [JACKSON-850]
-            if (argCount == 0) {
-                if (isCreator) {
-                    creators.setDefaultCreator(factory);
-                }
-                continue;
-            }
-
-            final BeanPropertyDefinition[] propDefs = creatorParams.get(factory);
-            // some single-arg factory methods (String, number) are auto-detected
-            if (argCount == 1) {
-                BeanPropertyDefinition argDef = (propDefs == null) ? null : propDefs[0];
-                boolean useProps = _checkIfCreatorPropertyBased(intr, factory, argDef);
-                if (!useProps) { // not property based but delegating
-                    /*boolean added=*/ _handleSingleArgumentFactory(config, beanDesc, vchecker, intr, creators,
-                            factory, isCreator);
-                    // 23-Sep-2016, tatu: [databind#1383]: Need to also sever link to avoid possible
-                    //    later problems with "unresolved" constructor property
-                    if (argDef != null) {
-                        ((POJOPropertyBuilder) argDef).removeConstructors();
-                    }
-                    continue;
-                }
-                // fall through if there's name
-            } else {
-                // more than 2 args, must have @JsonCreator
-                if (!isCreator) {
-                    continue;
-                }
-            }
-            // 1 or more args; all params must have name annotations
-            AnnotatedParameter nonAnnotatedParam = null;            
-            SettableBeanProperty[] properties = new SettableBeanProperty[argCount];
-            int implicitNameCount = 0;
-            int explicitNameCount = 0;
-            int injectCount = 0;
-            
-            for (int i = 0; i < argCount; ++i) {
-                final AnnotatedParameter param = factory.getParameter(i);
-                BeanPropertyDefinition propDef = (propDefs == null) ? null : propDefs[i];
-                Object injectId = intr.findInjectableValueId(param);
-                final PropertyName name = (propDef == null) ? null : propDef.getFullName();
-
-                if (propDef != null && propDef.isExplicitlyNamed()) {
-                    ++explicitNameCount;
-                    properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectId);
-                    continue;
-                }
-                if (injectId != null) {
-                    ++injectCount;
-                    properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectId);
-                    continue;
-                }
-                NameTransformer unwrapper = intr.findUnwrappingNameTransformer(param);
-                if (unwrapper != null) {
-                    properties[i] = constructCreatorProperty(ctxt, beanDesc, UNWRAPPED_CREATOR_PARAM_NAME, i, param, null);
-                    ++implicitNameCount;
-                    continue;
-                }
-                // One more thing: implicit names are ok iff ctor has creator annotation
-                if (isCreator) {
-                    if (name != null && !name.isEmpty()) {
-                        ++implicitNameCount;
-                        properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectId);
-                        continue;
-                    }
-                }
-                /* 25-Sep-2014, tatu: Actually, we may end up "losing" naming due to higher-priority constructor
-                 *  (see TestCreators#testConstructorCreator() test). And just to avoid running into that problem,
-                 *  let's add one more work around
-                 */
-                /*
-                PropertyName name2 = _findExplicitParamName(param, intr);
-                if (name2 != null && !name2.isEmpty()) {
-                    // Hmmh. Ok, fine. So what are we to do with it... ?
-                    // For now... skip. May need to revisit this, should this become problematic
-                    continue main_loop;
-                }
-                */
-                if (nonAnnotatedParam == null) {
-                    nonAnnotatedParam = param;
-                }
-            }
-            final int namedCount = explicitNameCount + implicitNameCount;
-            
-            // Ok: if named or injectable, we have more work to do
-            if (isCreator || explicitNameCount > 0 || injectCount > 0) {
-                // simple case; everything covered:
-                if ((namedCount + injectCount) == argCount) {
-                    creators.addPropertyCreator(factory, isCreator, properties);
-                } else if ((explicitNameCount == 0) && ((injectCount + 1) == argCount)) {
-                    // [712] secondary: all but one injectable, one un-annotated (un-named)
-                    creators.addDelegatingCreator(factory, isCreator, properties);
-                } else { // otherwise, epic fail
-                    throw new IllegalArgumentException("Argument #"+nonAnnotatedParam.getIndex()
-                            +" of factory method "+factory+" has no property name annotation; must have name when multiple-parameter constructor annotated as Creator");
-                }
-            }
-        }
-    }
-
-    protected boolean _handleSingleArgumentFactory(DeserializationConfig config,
-            BeanDescription beanDesc, VisibilityChecker<?> vchecker,
-            AnnotationIntrospector intr, CreatorCollector creators,
-            AnnotatedMethod factory, boolean isCreator)
-        throws JsonMappingException
-    {
-        Class<?> type = factory.getRawParameterType(0);
-        
-        if (type == String.class || type == CharSequence.class) {
-            if (isCreator || vchecker.isCreatorVisible(factory)) {
-                creators.addStringCreator(factory, isCreator);
-            }
-            return true;
-        }
-        if (type == int.class || type == Integer.class) {
-            if (isCreator || vchecker.isCreatorVisible(factory)) {
-                creators.addIntCreator(factory, isCreator);
-            }
-            return true;
-        }
-        if (type == long.class || type == Long.class) {
-            if (isCreator || vchecker.isCreatorVisible(factory)) {
-                creators.addLongCreator(factory, isCreator);
-            }
-            return true;
-        }
-        if (type == double.class || type == Double.class) {
-            if (isCreator || vchecker.isCreatorVisible(factory)) {
-                creators.addDoubleCreator(factory, isCreator);
-            }
-            return true;
-        }
-        if (type == boolean.class || type == Boolean.class) {
-            if (isCreator || vchecker.isCreatorVisible(factory)) {
-                creators.addBooleanCreator(factory, isCreator);
-            }
-            return true;
-        }
-        if (isCreator) {
-            creators.addDelegatingCreator(factory, isCreator, null);
-            return true;
-        }
-        return false;
+        ctxt.reportBadDefinition(beanDesc.getType(), String.format(
+                "Cannot define Creator parameter %d as `@JsonUnwrapped`: combination not yet supported",
+                param.getIndex()));
     }
 
     /**
@@ -834,7 +1037,7 @@
     protected SettableBeanProperty constructCreatorProperty(DeserializationContext ctxt,
             BeanDescription beanDesc, PropertyName name, int index,
             AnnotatedParameter param,
-            Object injectableValueId)
+            JacksonInject.Value injectable)
         throws JsonMappingException
     {
         final DeserializationConfig config = ctxt.getConfig();
@@ -853,8 +1056,7 @@
         }
         JavaType type = resolveMemberAndTypeAnnotations(ctxt, param, param.getType());
         BeanProperty.Std property = new BeanProperty.Std(name, type,
-                intr.findWrapperName(param),
-                beanDesc.getClassAnnotations(), param, metadata);
+                intr.findWrapperName(param), param, metadata);
         // Type deserializer: either comes from property (and already resolved)
         TypeDeserializer typeDeser = (TypeDeserializer) type.getTypeHandler();
         // or if not, based on type being referenced:
@@ -863,6 +1065,9 @@
         }
         // Note: contextualization of typeDeser _should_ occur in constructor of CreatorProperty
         // so it is not called directly here
+
+        Object injectableValueId = (injectable == null) ? null : injectable.getId();
+        
         SettableBeanProperty prop = new CreatorProperty(name, type, property.getWrapperName(),
                 typeDeser, beanDesc.getClassAnnotations(), param, index, injectableValueId,
                 metadata);
@@ -878,7 +1083,7 @@
         return prop;
     }
 
-    protected PropertyName _findParamName(AnnotatedParameter param, AnnotationIntrospector intr)
+    private PropertyName _findParamName(AnnotatedParameter param, AnnotationIntrospector intr)
     {
         if (param != null && intr != null) {
             PropertyName name = intr.findNameForDeserialization(param);
@@ -896,34 +1101,6 @@
         return null;
     }
 
-    protected PropertyName _findImplicitParamName(AnnotatedParameter param, AnnotationIntrospector intr)
-    {
-        String str = intr.findImplicitPropertyName(param);
-        if (str != null && !str.isEmpty()) {
-            return PropertyName.construct(str);
-        }
-        return null;
-    }
-
-    @Deprecated // in 2.6, remove from 2.7
-    protected PropertyName _findExplicitParamName(AnnotatedParameter param, AnnotationIntrospector intr)
-    {
-        if (param != null && intr != null) {
-            return intr.findNameForDeserialization(param);
-        }
-        return null;
-    }
-
-    @Deprecated // in 2.6, remove from 2.7
-    protected boolean _hasExplicitParamName(AnnotatedParameter param, AnnotationIntrospector intr)
-    {
-        if (param != null && intr != null) {
-            PropertyName n = intr.findNameForDeserialization(param);
-            return (n != null) && n.hasSimpleName();
-        }
-        return false;
-    }
-
     /*
     /**********************************************************
     /* JsonDeserializerFactory impl: array deserializers
@@ -954,7 +1131,8 @@
                 Class<?> raw = elemType.getRawClass();
                 if (elemType.isPrimitive()) {
                     return PrimitiveArrayDeserializers.forType(raw);
-                } else if (raw == String.class) {
+                }
+                if (raw == String.class) {
                     return StringArrayDeserializer.instance;
                 }
             }
@@ -1019,7 +1197,7 @@
                 if (implType == null) {
                     // [databind#292]: Actually, may be fine, but only if polymorphich deser enabled
                     if (type.getTypeHandler() == null) {
-                        throw new IllegalArgumentException("Can not find a deserializer for non-concrete Collection type "+type);
+                        throw new IllegalArgumentException("Cannot find a deserializer for non-concrete Collection type "+type);
                     }
                     deser = AbstractDeserializer.constructForNonPOJO(beanDesc);
                 } else {
@@ -1032,12 +1210,17 @@
                 ValueInstantiator inst = findValueInstantiator(ctxt, beanDesc);
                 if (!inst.canCreateUsingDefault()) {
                     // [databind#161]: No default constructor for ArrayBlockingQueue...
-                    if (type.getRawClass() == ArrayBlockingQueue.class) {
+                    if (type.hasRawClass(ArrayBlockingQueue.class)) {
                         return new ArrayBlockingQueueDeserializer(type, contentDeser, contentTypeDeser, inst);
                     }
+                    // 10-Jan-2017, tatu: `java.util.Collections` types need help:
+                    deser = JavaUtilCollectionsDeserializers.findForCollection(ctxt, type);
+                    if (deser != null) {
+                        return deser;
+                    }
                 }
                 // Can use more optimal deserializer if content type is String, so:
-                if (contentType.getRawClass() == String.class) {
+                if (contentType.hasRawClass(String.class)) {
                     // no value type deserializer because Strings are one of natural/native types:
                     deser = new StringCollectionDeserializer(type, contentDeser, inst);
                 } else {
@@ -1130,11 +1313,21 @@
             // Value handling is identical for all, but EnumMap requires special handling for keys
             Class<?> mapClass = type.getRawClass();
             if (EnumMap.class.isAssignableFrom(mapClass)) {
+                ValueInstantiator inst;
+
+                // 06-Mar-2017, tatu: Should only need to check ValueInstantiator for
+                //    custom sub-classes, see [databind#1544]
+                if (mapClass == EnumMap.class) {
+                    inst = null;
+                } else {
+                    inst = findValueInstantiator(ctxt, beanDesc);
+                }
                 Class<?> kt = keyType.getRawClass();
                 if (kt == null || !kt.isEnum()) {
-                    throw new IllegalArgumentException("Can not construct EnumMap; generic (key) type not available");
+                    throw new IllegalArgumentException("Cannot construct EnumMap; generic (key) type not available");
                 }
-                deser = new EnumMapDeserializer(type, null, contentDeser, contentTypeDeser);
+                deser = new EnumMapDeserializer(type, inst, null,
+                        contentDeser, contentTypeDeser, null);
             }
 
             // Otherwise, generic handler works ok.
@@ -1160,10 +1353,16 @@
                     } else {
                         // [databind#292]: Actually, may be fine, but only if polymorphic deser enabled
                         if (type.getTypeHandler() == null) {
-                            throw new IllegalArgumentException("Can not find a deserializer for non-concrete Map type "+type);
+                            throw new IllegalArgumentException("Cannot find a deserializer for non-concrete Map type "+type);
                         }
                         deser = AbstractDeserializer.constructForNonPOJO(beanDesc);
                     }
+                } else {
+                    // 10-Jan-2017, tatu: `java.util.Collections` types need help:
+                    deser = JavaUtilCollectionsDeserializers.findForMap(ctxt, type);
+                    if (deser != null) {
+                        return deser;
+                    }
                 }
                 if (deser == null) {
                     ValueInstantiator inst = findValueInstantiator(ctxt, beanDesc);
@@ -1254,7 +1453,7 @@
                     : valueInstantiator.getFromObjectArguments(ctxt.getConfig());
             // May have @JsonCreator for static factory method:
             for (AnnotatedMethod factory : beanDesc.getFactoryMethods()) {
-                if (ctxt.getAnnotationIntrospector().hasCreatorAnnotation(factory)) {
+                if (_hasCreatorAnnotation(ctxt, factory)) {
                     if (factory.getParameterCount() == 0) { // [databind#960]
                         deser = EnumDeserializer.deserializerForNoArgsCreator(config, enumClass, factory);
                         break;
@@ -1271,7 +1470,8 @@
             // Need to consider @JsonValue if one found
             if (deser == null) {
                 deser = new EnumDeserializer(constructEnumResolver(enumClass,
-                        config, beanDesc.findJsonValueMethod()));
+                        config, beanDesc.findJsonValueAccessor()),
+                        config.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS));
             }
         }
 
@@ -1319,8 +1519,19 @@
 
         if (deser == null) {
             // Just one referential type as of JDK 1.7 / Java 7: AtomicReference (Java 8 adds Optional)
-            if (AtomicReference.class.isAssignableFrom(type.getRawClass())) {
-                return new AtomicReferenceDeserializer(type, contentTypeDeser, contentDeser);
+            if (type.isTypeOrSubTypeOf(AtomicReference.class)) {
+                Class<?> rawType = type.getRawClass();
+                ValueInstantiator inst;
+                if (rawType == AtomicReference.class) {
+                    inst = null;
+                } else {
+                    /* 23-Oct-2016, tatu: Note that subtypes are probably not supportable
+                     *    without either forcing merging (to avoid having to create instance)
+                     *    or something else...
+                     */
+                    inst = findValueInstantiator(ctxt, beanDesc);
+                }
+                return new AtomicReferenceDeserializer(type, inst, contentTypeDeser, contentDeser);
             }
         }
         if (deser != null) {
@@ -1350,9 +1561,8 @@
         AnnotationIntrospector ai = config.getAnnotationIntrospector();
         TypeResolverBuilder<?> b = ai.findTypeResolver(config, ac, baseType);
 
-        /* Ok: if there is no explicit type info handler, we may want to
-         * use a default. If so, config object knows what to use.
-         */
+        // Ok: if there is no explicit type info handler, we may want to
+        // use a default. If so, config object knows what to use.
         Collection<NamedType> subtypes = null;
         if (b == null) {
             b = config.getDefaultTyper(baseType);
@@ -1366,11 +1576,20 @@
         // (note: check for abstract type is not 100% mandatory, more of an optimization)
         if ((b.getDefaultImpl() == null) && baseType.isAbstract()) {
             JavaType defaultType = mapAbstractType(config, baseType);
-            if (defaultType != null && defaultType.getRawClass() != baseType.getRawClass()) {
+            if ((defaultType != null) && !defaultType.hasRawClass(baseType.getRawClass())) {
                 b = b.defaultImpl(defaultType.getRawClass());
             }
         }
-        return b.buildTypeDeserializer(config, baseType, subtypes);
+        // 05-Apt-2018, tatu: Since we get non-mapping exception due to various limitations,
+        //    map to better type here
+        try {
+            return b.buildTypeDeserializer(config, baseType, subtypes);
+        } catch (IllegalArgumentException e0) {
+            InvalidDefinitionException e = InvalidDefinitionException.from((JsonParser) null,
+                    e0.getMessage(), baseType);
+            e.initCause(e0);
+            throw e;
+        }
     }
 
     /**
@@ -1449,11 +1668,10 @@
                 return StdKeyDeserializers.constructDelegatingKeyDeserializer(config, type, valueDesForKey);
             }
         }
-        EnumResolver enumRes = constructEnumResolver(enumClass, config, beanDesc.findJsonValueMethod());
+        EnumResolver enumRes = constructEnumResolver(enumClass, config, beanDesc.findJsonValueAccessor());
         // May have @JsonCreator for static factory method:
-        final AnnotationIntrospector ai = config.getAnnotationIntrospector();
         for (AnnotatedMethod factory : beanDesc.getFactoryMethods()) {
-            if (ai.hasCreatorAnnotation(factory)) {
+            if (_hasCreatorAnnotation(ctxt, factory)) {
                 int argCount = factory.getParameterCount();
                 if (argCount == 1) {
                     Class<?> returnType = factory.getRawReturnType();
@@ -1567,7 +1785,7 @@
             }
             return new UntypedObjectDeserializer(lt, mt);
         }
-        if (rawType == CLASS_STRING || rawType == CLASS_CHAR_BUFFER) {
+        if (rawType == CLASS_STRING || rawType == CLASS_CHAR_SEQUENCE) {
             return StringDeserializer.instance;
         }
         if (rawType == CLASS_ITERABLE) {
@@ -1581,14 +1799,8 @@
         }
         if (rawType == CLASS_MAP_ENTRY) {
             // 28-Apr-2015, tatu: TypeFactory does it all for us already so
-            JavaType kt = type.containedType(0);
-            if (kt == null) {
-                kt = TypeFactory.unknownType();
-            }
-            JavaType vt = type.containedType(1);
-            if (vt == null) {
-                vt = TypeFactory.unknownType();
-            }
+            JavaType kt = type.containedTypeOrUnknown(0);
+            JavaType vt = type.containedTypeOrUnknown(1);
             TypeDeserializer vts = (TypeDeserializer) vt.getTypeHandler();
             if (vts == null) {
                 vts = findTypeDeserializer(ctxt.getConfig(), vt);
@@ -1810,6 +2022,23 @@
     }
 
     /**
+     * @since 2.9
+     */
+    protected JsonDeserializer<Object> findContentDeserializerFromAnnotation(DeserializationContext ctxt,
+            Annotated ann)
+        throws JsonMappingException
+    {
+        AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
+        if (intr != null) {
+            Object deserDef = intr.findContentDeserializer(ann);
+            if (deserDef != null) {
+                return ctxt.deserializerInstance(ann, deserDef);
+            }
+        }
+        return null;
+    }
+    
+    /**
      * Helper method used to resolve additional type-related annotation information
      * like type overrides, or handler (serializer, deserializer) overrides,
      * so that from declared field, property or constructor parameter type
@@ -1870,20 +2099,34 @@
     }
 
     protected EnumResolver constructEnumResolver(Class<?> enumClass,
-            DeserializationConfig config, AnnotatedMethod jsonValueMethod)
+            DeserializationConfig config, AnnotatedMember jsonValueAccessor)
     {
-        if (jsonValueMethod != null) {
-            Method accessor = jsonValueMethod.getAnnotated();
+        if (jsonValueAccessor != null) {
             if (config.canOverrideAccessModifiers()) {
-                ClassUtil.checkAndFixAccess(accessor, config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
+                ClassUtil.checkAndFixAccess(jsonValueAccessor.getMember(),
+                        config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
             }
-            return EnumResolver.constructUnsafeUsingMethod(enumClass, accessor, config.getAnnotationIntrospector());
+            return EnumResolver.constructUnsafeUsingMethod(enumClass,
+                    jsonValueAccessor, config.getAnnotationIntrospector());
         }
         // 14-Mar-2016, tatu: We used to check `DeserializationFeature.READ_ENUMS_USING_TO_STRING`
         //   here, but that won't do: it must be dynamically changeable...
         return EnumResolver.constructUnsafe(enumClass, config.getAnnotationIntrospector());
     }
 
+    /**
+     * @since 2.9
+     */
+    protected boolean _hasCreatorAnnotation(DeserializationContext ctxt,
+            Annotated ann) {
+        AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
+        if (intr != null) {
+            JsonCreator.Mode mode = intr.findCreatorAnnotation(ctxt.getConfig(), ann);
+            return (mode != null) && (mode != JsonCreator.Mode.DISABLED); 
+        }
+        return false;
+    }
+    
     /*
     /**********************************************************
     /* Deprecated helper methods
@@ -1905,36 +2148,6 @@
         if (intr == null) {
             return type;
         }
-
-        // First, deserializers for key/value types?
-        /*
-        if (type.isMapLikeType()) {
-            JavaType keyType = type.getKeyType();
-            // 21-Mar-2011, tatu: ... and associated deserializer too (unless already assigned)
-            //  (not 100% why or how, but this does seem to get called more than once, which
-            //   is not good: for now, let's just avoid errors)
-            if (keyType != null && keyType.getValueHandler() == null) {
-                Object kdDef = intr.findKeyDeserializer(a);
-                KeyDeserializer kd = ctxt.keyDeserializerInstance(a, kdDef);
-                if (kd != null) {
-                    type = (T) ((MapLikeType) type).withKeyValueHandler(kd);
-                    keyType = type.getKeyType(); // just in case it's used below
-                }
-            }            
-        }
-        JavaType contentType = type.getContentType();
-        if (contentType != null) {
-           // ... as well as deserializer for contents:
-           if (contentType.getValueHandler() == null) { // as with above, avoid resetting (which would trigger exception)
-               Object cdDef = intr.findContentDeserializer(a);
-                JsonDeserializer<?> cd = ctxt.deserializerInstance(a, cdDef);
-                if (cd != null) {
-                    type = (T) type.withContentValueHandler(cd);
-                }
-            }
-        }
-        */
-        // then: type refinement(s)?
         return intr.refineDeserializationType(ctxt.getConfig(), a, type);
     }
 
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java
index 76603d5..c53bebc 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java
@@ -20,7 +20,7 @@
 {
     /* TODOs for future versions:
      * 
-     * For 2.8?
+     * For 2.9?
      *
      * - New method in JsonDeserializer (deserializeNext()) to allow use of more
      *   efficient 'nextXxx()' method `JsonParser` provides.
@@ -35,10 +35,18 @@
      * Lazily constructed exception used as root cause if reporting problem
      * with creator method that returns <code>null</code> (which is not allowed)
      *
-     * @since 3.8
+     * @since 2.8
      */
     protected transient Exception _nullFromCreator;
-    
+
+    /**
+     * State marker we need in order to avoid infinite recursion for some cases
+     * (not very clean, alas, but has to do for now)
+     *
+     * @since 2.9
+     */
+    private volatile transient NameTransformer _currentlyTransforming;
+
     /*
     /**********************************************************
     /* Life-cycle, construction, initialization
@@ -86,19 +94,22 @@
     }
 
     @Override
-    public JsonDeserializer<Object> unwrappingDeserializer(NameTransformer unwrapper)
+    public JsonDeserializer<Object> unwrappingDeserializer(NameTransformer transformer)
     {
-        /* bit kludgy but we don't want to accidentally change type; sub-classes
-         * MUST override this method to support unwrapped properties...
-         */
+        // bit kludgy but we don't want to accidentally change type; sub-classes
+        // MUST override this method to support unwrapped properties...
         if (getClass() != BeanDeserializer.class) {
             return this;
         }
-        /* main thing really is to just enforce ignoring of unknown
-         * properties; since there may be multiple unwrapped values
-         * and properties for all may be interleaved...
-         */
-        return new BeanDeserializer(this, unwrapper);
+        // 25-Mar-2017, tatu: Not clean at all, but for [databind#383] we do need
+        //   to keep track of accidental recursion...
+        if (_currentlyTransforming == transformer) {
+            return this;
+        }
+        _currentlyTransforming = transformer;
+        try {
+            return new BeanDeserializer(this, transformer);
+        } finally { _currentlyTransforming = null; }
     }
 
     @Override
@@ -154,34 +165,35 @@
             JsonToken t) throws IOException
     {
         // and then others, generally requiring use of @JsonCreator
-        switch (t) {
-        case VALUE_STRING:
-            return deserializeFromString(p, ctxt);
-        case VALUE_NUMBER_INT:
-            return deserializeFromNumber(p, ctxt);
-        case VALUE_NUMBER_FLOAT:
-	    return deserializeFromDouble(p, ctxt);
-        case VALUE_EMBEDDED_OBJECT:
-            return deserializeFromEmbedded(p, ctxt);
-        case VALUE_TRUE:
-        case VALUE_FALSE:
-            return deserializeFromBoolean(p, ctxt);
-
-        case VALUE_NULL:
-            return deserializeFromNull(p, ctxt);
-        case START_ARRAY:
-            // these only work if there's a (delegating) creator...
-            return deserializeFromArray(p, ctxt);
-        case FIELD_NAME:
-        case END_OBJECT: // added to resolve [JACKSON-319], possible related issues
-            if (_vanillaProcessing) {
-                return vanillaDeserialize(p, ctxt, t);
+        if (t != null) {
+            switch (t) {
+            case VALUE_STRING:
+                return deserializeFromString(p, ctxt);
+            case VALUE_NUMBER_INT:
+                return deserializeFromNumber(p, ctxt);
+            case VALUE_NUMBER_FLOAT:
+                return deserializeFromDouble(p, ctxt);
+            case VALUE_EMBEDDED_OBJECT:
+                return deserializeFromEmbedded(p, ctxt);
+            case VALUE_TRUE:
+            case VALUE_FALSE:
+                return deserializeFromBoolean(p, ctxt);
+            case VALUE_NULL:
+                return deserializeFromNull(p, ctxt);
+            case START_ARRAY:
+                // these only work if there's a (delegating) creator...
+                return deserializeFromArray(p, ctxt);
+            case FIELD_NAME:
+            case END_OBJECT: // added to resolve [JACKSON-319], possible related issues
+                if (_vanillaProcessing) {
+                    return vanillaDeserialize(p, ctxt, t);
+                }
+                if (_objectIdReader != null) {
+                    return deserializeWithObjectId(p, ctxt);
+                }
+                return deserializeFromObject(p, ctxt);
+            default:
             }
-            if (_objectIdReader != null) {
-                return deserializeWithObjectId(p, ctxt);
-            }
-            return deserializeFromObject(p, ctxt);
-        default:
         }
         return ctxt.handleUnexpectedToken(handledType(), p);
     }
@@ -291,14 +303,14 @@
     @Override
     public Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) throws IOException
     {
-        /* 09-Dec-2014, tatu: As per [#622], we need to allow Object Id references
+        /* 09-Dec-2014, tatu: As per [databind#622], we need to allow Object Id references
          *   to come in as JSON Objects as well; but for now assume they will
          *   be simple, single-property references, which means that we can
          *   recognize them without having to buffer anything.
          *   Once again, if we must, we can do more complex handling with buffering,
          *   but let's only do that if and when that becomes necessary.
          */
-        if (_objectIdReader != null && _objectIdReader.maySerializeAsObject()) {
+        if ((_objectIdReader != null) && _objectIdReader.maySerializeAsObject()) {
             if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME)
                     && _objectIdReader.isValidReferencePropertyName(p.getCurrentName(), p)) {
                 return deserializeFromObjectId(p, ctxt);
@@ -381,8 +393,8 @@
     {
         final PropertyBasedCreator creator = _propertyBasedCreator;
         PropertyValueBuffer buffer = creator.startBuilding(p, ctxt, _objectIdReader);
-
         TokenBuffer unknown = null;
+        final Class<?> activeView = _needViewProcesing ? ctxt.getActiveView() : null;
 
         JsonToken t = p.getCurrentToken();
         List<BeanReferring> referrings = null;
@@ -397,8 +409,13 @@
             SettableBeanProperty creatorProp = creator.findCreatorProperty(propName);
             if (creatorProp != null) {
                 // Last creator property to set?
-                if (buffer.assignParameter(creatorProp,
-                        _deserializeWithErrorWrapping(p, ctxt, creatorProp))) {
+                Object value;
+                if ((activeView != null) && !creatorProp.visibleInView(activeView)) {
+                    p.skipChildren();
+                    continue;
+                }
+                value = _deserializeWithErrorWrapping(p, ctxt, creatorProp);
+                if (buffer.assignParameter(creatorProp, value)) {
                     p.nextToken(); // to move to following FIELD_NAME/END_OBJECT
                     Object bean;
                     try {
@@ -616,7 +633,7 @@
             p.nextToken();
             SettableBeanProperty prop = _beanProperties.find(propName);
             if (prop != null) { // normal case
-                if (activeView != null && !prop.visibleInView(activeView)) {
+                if ((activeView != null) && !prop.visibleInView(activeView)) {
                     p.skipChildren();
                     continue;
                 }
@@ -640,21 +657,17 @@
                 // but... others should be passed to unwrapped property deserializers
                 tokens.writeFieldName(propName);
                 tokens.copyCurrentStructure(p);
-            } else {
-                // Need to copy to a separate buffer first
-                TokenBuffer b2 = new TokenBuffer(p, ctxt);
-                b2.copyCurrentStructure(p);
-                tokens.writeFieldName(propName);
-                tokens.append(b2);
-                try {
-                    JsonParser p2 = b2.asParser(p);
-                    p2.nextToken();
-                    _anySetter.deserializeAndSet(p2, ctxt, bean, propName);
-                } catch (Exception e) {
-                    wrapAndThrow(e, bean, propName, ctxt);
-                }
                 continue;
             }
+            // Need to copy to a separate buffer first
+            TokenBuffer b2 = TokenBuffer.asCopyOfValue(p);
+            tokens.writeFieldName(propName);
+            tokens.append(b2);
+            try {
+                _anySetter.deserializeAndSet(b2.asParserOnFirstToken(), ctxt, bean, propName);
+            } catch (Exception e) {
+                wrapAndThrow(e, bean, propName, ctxt);
+            }
         }
         tokens.writeEndObject();
         _unwrappedPropertyHandler.processUnwrapped(p, ctxt, bean, tokens);
@@ -662,7 +675,8 @@
     }
 
     @SuppressWarnings("resource")
-    protected Object deserializeWithUnwrapped(JsonParser p, DeserializationContext ctxt, Object bean)
+    protected Object deserializeWithUnwrapped(JsonParser p, DeserializationContext ctxt,
+            Object bean)
         throws IOException
     {
         JsonToken t = p.getCurrentToken();
@@ -702,14 +716,11 @@
                 tokens.copyCurrentStructure(p);
             } else {
                 // Need to copy to a separate buffer first
-                TokenBuffer b2 = new TokenBuffer(p, ctxt);
-                b2.copyCurrentStructure(p);
+                TokenBuffer b2 = TokenBuffer.asCopyOfValue(p);
                 tokens.writeFieldName(propName);
                 tokens.append(b2);
                 try {
-                    JsonParser p2 = b2.asParser(p);
-                    p2.nextToken();
-                    _anySetter.deserializeAndSet(p2, ctxt, bean, propName);
+                    _anySetter.deserializeAndSet(b2.asParserOnFirstToken(), ctxt, bean, propName);
                 } catch (Exception e) {
                     wrapAndThrow(e, bean, propName, ctxt);
                 }
@@ -725,6 +736,10 @@
     protected Object deserializeUsingPropertyBasedWithUnwrapped(JsonParser p, DeserializationContext ctxt)
         throws IOException
     {
+        // 01-Dec-2016, tatu: Note: This IS legal to call, but only when unwrapped
+        //    value itself is NOT passed via `CreatorProperty` (which isn't supported).
+        //    Ok however to pass via setter or field.
+        
         final PropertyBasedCreator creator = _propertyBasedCreator;
         PropertyValueBuffer buffer = creator.startBuilding(p, ctxt, _objectIdReader);
 
@@ -739,7 +754,8 @@
             SettableBeanProperty creatorProp = creator.findCreatorProperty(propName);
             if (creatorProp != null) {
                 // Last creator property to set?
-                if (buffer.assignParameter(creatorProp, _deserializeWithErrorWrapping(p, ctxt, creatorProp))) {
+                if (buffer.assignParameter(creatorProp,
+                        _deserializeWithErrorWrapping(p, ctxt, creatorProp))) {
                     t = p.nextToken(); // to move to following FIELD_NAME/END_OBJECT
                     Object bean;
                     try {
@@ -759,8 +775,8 @@
                     if (bean.getClass() != _beanType.getRawClass()) {
                         // !!! 08-Jul-2011, tatu: Could probably support; but for now
                         //   it's too complicated, so bail out
-                        tokens.close();
-                        ctxt.reportMappingException("Can not create polymorphic instances with unwrapped values");
+                        ctxt.reportInputMismatch(creatorProp,
+                                "Cannot create polymorphic instances with unwrapped values");
                         return null;
                     }
                     return _unwrappedPropertyHandler.processUnwrapped(p, ctxt, bean, tokens);
@@ -792,15 +808,12 @@
                 tokens.copyCurrentStructure(p);
             } else {
                 // Need to copy to a separate buffer first
-                TokenBuffer b2 = new TokenBuffer(p, ctxt);
-                b2.copyCurrentStructure(p);
+                TokenBuffer b2 = TokenBuffer.asCopyOfValue(p);
                 tokens.writeFieldName(propName);
                 tokens.append(b2);
                 try {
-                    JsonParser p2 = b2.asParser(p);
-                    p2.nextToken();
                     buffer.bufferAnyProperty(_anySetter, propName,
-                            _anySetter.deserialize(p2, ctxt));
+                            _anySetter.deserialize(b2.asParserOnFirstToken(), ctxt));
                 } catch (Exception e) {
                     wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt);
                 }
@@ -940,8 +953,9 @@
                         if (bean.getClass() != _beanType.getRawClass()) {
                             // !!! 08-Jul-2011, tatu: Could theoretically support; but for now
                             //   it's too complicated, so bail out
-                            ctxt.reportMappingException("Can not create polymorphic instances with external type ids");
-                            return null;
+                            return ctxt.reportBadDefinition(_beanType, String.format(
+                                    "Cannot create polymorphic instances with external type ids (%s -> %s)",
+                                    _beanType, bean.getClass()));
                         }
                         return ext.complete(p, ctxt, bean);
                     }
@@ -969,9 +983,11 @@
             }
             // "any property"?
             if (_anySetter != null) {
-                buffer.bufferAnyProperty(_anySetter, propName, _anySetter.deserialize(p, ctxt));
+                buffer.bufferAnyProperty(_anySetter, propName,
+                        _anySetter.deserialize(p, ctxt));
             }
         }
+        tokens.writeEndObject();
 
         // We hit END_OBJECT; resolve the pieces:
         try {
@@ -1019,8 +1035,8 @@
         public void handleResolvedForwardReference(Object id, Object value) throws IOException
         {
             if (_bean == null) {
-                _context.reportMappingException(
-"Can not resolve ObjectId forward reference using property '%s' (of type %s): Bean not yet resolved",
+                _context.reportInputMismatch(_prop,
+"Cannot resolve ObjectId forward reference using property '%s' (of type %s): Bean not yet resolved",
 _prop.getName(), _prop.getDeclaringClass().getName());
         }
             _prop.set(_bean, value);
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java
index 63eafbb..6ce41f7 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java
@@ -26,6 +26,7 @@
 public abstract class BeanDeserializerBase
     extends StdDeserializer<Object>
     implements ContextualDeserializer, ResolvableDeserializer,
+        ValueInstantiator.Gettable, // since 2.9
         java.io.Serializable // since 2.1
 {
     private static final long serialVersionUID = 1;
@@ -39,16 +40,6 @@
      */
 
     /**
-     * Annotations from the bean class: used for accessing
-     * annotations during resolution
-     * (see {@link #resolve}) and
-     * contextualization (see {@link #createContextual})
-     *<p> 
-     * Transient since annotations only used during construction.
-     */
-    final private transient Annotations _classAnnotations;
-
-    /**
      * Declared type of the bean this deserializer handles.
      */
     final protected JavaType _beanType;
@@ -57,7 +48,7 @@
      * Requested shape from bean class annotations.
      */
     final protected JsonFormat.Shape _serializationShape;
-    
+
     /*
     /**********************************************************
     /* Configuration for creating value instance
@@ -150,13 +141,13 @@
      * on active view used (if any)
      */
     final protected boolean _needViewProcesing;
-    
+
     /**
      * We may also have one or more back reference fields (usually
      * zero or one).
      */
     final protected Map<String, SettableBeanProperty> _backRefs;
-    
+
     /*
     /**********************************************************
     /* Related handlers
@@ -208,9 +199,6 @@
             boolean hasViews)
     {
         super(beanDesc.getType());
-
-        AnnotatedClass ac = beanDesc.getClassInfo();
-        _classAnnotations = ac.getAnnotations();       
         _beanType = beanDesc.getType();
         _valueInstantiator = builder.getValueInstantiator();
         
@@ -252,7 +240,6 @@
     {
         super(src._beanType);
         
-        _classAnnotations = src._classAnnotations;
         _beanType = src._beanType;
         
         _valueInstantiator = src._valueInstantiator;
@@ -274,12 +261,11 @@
 
         _vanillaProcessing = src._vanillaProcessing;
     }
- 
+
     protected BeanDeserializerBase(BeanDeserializerBase src, NameTransformer unwrapper)
     {
         super(src._beanType);
 
-        _classAnnotations = src._classAnnotations;
         _beanType = src._beanType;
         
         _valueInstantiator = src._valueInstantiator;
@@ -317,8 +303,6 @@
     public BeanDeserializerBase(BeanDeserializerBase src, ObjectIdReader oir)
     {
         super(src._beanType);
-        
-        _classAnnotations = src._classAnnotations;
         _beanType = src._beanType;
         
         _valueInstantiator = src._valueInstantiator;
@@ -356,19 +340,18 @@
     public BeanDeserializerBase(BeanDeserializerBase src, Set<String> ignorableProps)
     {
         super(src._beanType);
-        _classAnnotations = src._classAnnotations;
         _beanType = src._beanType;
         
         _valueInstantiator = src._valueInstantiator;
         _delegateDeserializer = src._delegateDeserializer;
         _propertyBasedCreator = src._propertyBasedCreator;
-        
+
         _backRefs = src._backRefs;
         _ignorableProps = ignorableProps;
         _ignoreAllUnknown = src._ignoreAllUnknown;
         _anySetter = src._anySetter;
         _injectables = src._injectables;
-        
+
         _nonStandardCreation = src._nonStandardCreation;
         _unwrappedPropertyHandler = src._unwrappedPropertyHandler;
         _needViewProcesing = src._needViewProcesing;
@@ -388,14 +371,12 @@
     protected BeanDeserializerBase(BeanDeserializerBase src, BeanPropertyMap beanProps)
     {
         super(src._beanType);
-        
-        _classAnnotations = src._classAnnotations;
         _beanType = src._beanType;
-        
+
         _valueInstantiator = src._valueInstantiator;
         _delegateDeserializer = src._delegateDeserializer;
         _propertyBasedCreator = src._propertyBasedCreator;
-        
+
         _beanProperties = beanProps;
         _backRefs = src._backRefs;
         _ignorableProps = src._ignorableProps;
@@ -434,7 +415,7 @@
      * Fluent factory for creating a variant that can handle
      * POJO output as a JSON Array. Implementations may ignore this request
      * if no such input is possible.
-     * 
+     *
      * @since 2.1
      */
     protected abstract BeanDeserializerBase asArrayDeserializer();
@@ -451,8 +432,7 @@
      * This is needed to handle recursive and transitive dependencies.
      */
     @Override
-    public void resolve(DeserializationContext ctxt)
-        throws JsonMappingException
+    public void resolve(DeserializationContext ctxt) throws JsonMappingException
     {
         ExternalTypeHandler.Builder extTypes = null;
         // if ValueInstantiator can use "creator" approach, need to resolve it here...
@@ -460,6 +440,18 @@
 
         if (_valueInstantiator.canCreateFromObjectWith()) {
             creatorProps = _valueInstantiator.getFromObjectArguments(ctxt.getConfig());
+
+            // 22-Jan-2018, tatu: May need to propagate "ignorable" status (from `Access.READ_ONLY`
+            //     or perhaps class-ignorables) into Creator properties too. Can not just delete,
+            //     at this point, but is needed for further processing down the line
+            if (_ignorableProps != null) {
+                for (int i = 0, end = creatorProps.length; i < end; ++i) {
+                    SettableBeanProperty prop  = creatorProps[i];
+                    if (_ignorableProps.contains(prop.getName())) {
+                        creatorProps[i].markAsIgnorable();
+                    }
+                }
+            }
         } else {
             creatorProps = null;
         }
@@ -468,9 +460,11 @@
         // 24-Mar-2017, tatu: Looks like we may have to iterate over
         //   properties twice, to handle potential issues with recursive
         //   types (see [databind#1575] f.ex).
-
         // First loop: find deserializer if not yet known, but do not yet
         // contextualize (since that can lead to problems with self-references)
+        // 22-Jan-2018, tatu: NOTE! Need not check for `isIgnorable` as that can
+        //   only happen for props in `creatorProps`
+
         for (SettableBeanProperty prop : _beanProperties) {
             if (!prop.hasValueDeserializer()) {
                 // [databind#125]: allow use of converters
@@ -497,19 +491,30 @@
                 prop = _resolvedObjectIdProperty(ctxt, prop);
             }
             // Support unwrapped values (via @JsonUnwrapped)
-            SettableBeanProperty u = _resolveUnwrappedProperty(ctxt, prop);
-            if (u != null) {
-                prop = u;
-                if (unwrapped == null) {
-                    unwrapped = new UnwrappedPropertyHandler();
+            NameTransformer xform = _findPropertyUnwrapper(ctxt, prop);
+            if (xform != null) {
+                JsonDeserializer<Object> orig = prop.getValueDeserializer();
+                JsonDeserializer<Object> unwrapping = orig.unwrappingDeserializer(xform);
+                if (unwrapping != orig && unwrapping != null) {
+                    prop = prop.withValueDeserializer(unwrapping);
+                    if (unwrapped == null) {
+                        unwrapped = new UnwrappedPropertyHandler();
+                    }
+                    unwrapped.addProperty(prop);
+                    // 12-Dec-2014, tatu: As per [databind#647], we will have problems if
+                    //    the original property is left in place. So let's remove it now.
+                    // 25-Mar-2017, tatu: Wonder if this could be problematic wrt creators?
+                    //    (that is, should be remove it from creator too)
+                    _beanProperties.remove(prop);
+                    continue;
                 }
-                unwrapped.addProperty(prop);
-                // 12-Dec-2014, tatu: As per [databind#647], we will have problems if
-                //    the original property is left in place. So let's remove it now.
-                // 25-Mar-2017, tatu: Wonder if this could be problematic wrt creators?
-                _beanProperties.remove(prop);
-                continue;
             }
+
+            // 26-Oct-2016, tatu: Need to have access to value deserializer to know if
+            //   merging needed, and now seems to be reasonable time to do that.
+            final PropertyMetadata md = prop.getMetadata();
+            prop = _resolveMergeAndNullSettings(ctxt, prop, md);
+
             // non-static inner classes too:
             prop = _resolveInnerClassValuedProperty(ctxt, prop);
             if (prop != origProp) {
@@ -522,7 +527,7 @@
                 TypeDeserializer typeDeser = prop.getValueTypeDeserializer();
                 if (typeDeser.getTypeInclusion() == JsonTypeInfo.As.EXTERNAL_PROPERTY) {
                     if (extTypes == null) {
-                        extTypes = new ExternalTypeHandler.Builder();
+                        extTypes = ExternalTypeHandler.builder(_beanType);
                     }
                     extTypes.addExternal(prop, typeDeser);
                     // In fact, remove from list of known properties to simplify later handling
@@ -532,7 +537,7 @@
             }
         }
         // "any setter" may also need to be resolved now
-        if (_anySetter != null && !_anySetter.hasValueDeserializer()) {
+        if ((_anySetter != null) && !_anySetter.hasValueDeserializer()) {
             _anySetter = _anySetter.withValueDeserializer(findDeserializer(ctxt,
                     _anySetter.getType(), _anySetter.getProperty()));
         }
@@ -540,9 +545,9 @@
         if (_valueInstantiator.canCreateUsingDelegate()) {
             JavaType delegateType = _valueInstantiator.getDelegateType(ctxt.getConfig());
             if (delegateType == null) {
-                throw new IllegalArgumentException("Invalid delegate-creator definition for "+_beanType
-                        +": value instantiator ("+_valueInstantiator.getClass().getName()
-                        +") returned true for 'canCreateUsingDelegate()', but null for 'getDelegateType()'");
+                ctxt.reportBadDefinition(_beanType, String.format(
+"Invalid delegate-creator definition for %s: value instantiator (%s) returned true for 'canCreateUsingDelegate()', but null for 'getDelegateType()'",
+                    _beanType, _valueInstantiator.getClass().getName()));
             }
             _delegateDeserializer = _findDelegateDeserializer(ctxt, delegateType,
                     _valueInstantiator.getDelegateCreator());
@@ -552,9 +557,9 @@
         if (_valueInstantiator.canCreateUsingArrayDelegate()) {
             JavaType delegateType = _valueInstantiator.getArrayDelegateType(ctxt.getConfig());
             if (delegateType == null) {
-                throw new IllegalArgumentException("Invalid array-delegate-creator definition for "+_beanType
-                        +": value instantiator ("+_valueInstantiator.getClass().getName()
-                        +") returned true for 'canCreateUsingArrayDelegate()', but null for 'getArrayDelegateType()'");
+                ctxt.reportBadDefinition(_beanType, String.format(
+"Invalid delegate-creator definition for %s: value instantiator (%s) returned true for 'canCreateUsingArrayDelegate()', but null for 'getArrayDelegateType()'",
+                        _beanType, _valueInstantiator.getClass().getName()));
             }
             _arrayDelegateDeserializer = _findDelegateDeserializer(ctxt, delegateType,
                     _valueInstantiator.getArrayDelegateCreator());
@@ -562,7 +567,8 @@
 
         // And now that we know CreatorProperty instances are also resolved can finally create the creator:
         if (creatorProps != null) {
-            _propertyBasedCreator = PropertyBasedCreator.construct(ctxt, _valueInstantiator, creatorProps);
+            _propertyBasedCreator = PropertyBasedCreator.construct(ctxt, _valueInstantiator,
+                    creatorProps, _beanProperties);
         }
 
         if (extTypes != null) {
@@ -587,7 +593,7 @@
     protected void _replaceProperty(BeanPropertyMap props, SettableBeanProperty[] creatorProps,
             SettableBeanProperty origProp, SettableBeanProperty newProp)
     {
-        props.replace(newProp);
+        props.replace(origProp, newProp);
         // [databind#795]: Make sure PropertyBasedCreator's properties stay in sync
         if (creatorProps != null) {
             // 18-May-2015, tatu: _Should_ start with consistent set. But can we really
@@ -598,29 +604,39 @@
                     return;
                 }
             }
+            /*
             // ... as per above, it is possible we'd need to add this as fallback
             // if (but only if) identity check fails?
-            /*
-            if (creatorProps[i].getName().equals(prop.getName())) {
-                creatorProps[i] = prop;
-                break;
+            for (int i = 0, len = creatorProps.length; i < len; ++i) {
+                if (creatorProps[i].getName().equals(origProp.getName())) {
+                    creatorProps[i] = newProp;
+                    return;
+                }
             }
             */
         }
     }
 
-    private JsonDeserializer<Object> _findDelegateDeserializer(DeserializationContext ctxt, JavaType delegateType,
-            AnnotatedWithParams delegateCreator) throws JsonMappingException {
+    @SuppressWarnings("unchecked")
+    private JsonDeserializer<Object> _findDelegateDeserializer(DeserializationContext ctxt,
+            JavaType delegateType, AnnotatedWithParams delegateCreator) throws JsonMappingException
+    {
         // Need to create a temporary property to allow contextual deserializers:
         BeanProperty.Std property = new BeanProperty.Std(TEMP_PROPERTY_NAME,
-                delegateType, null, _classAnnotations, delegateCreator,
+                delegateType, null, delegateCreator,
                 PropertyMetadata.STD_OPTIONAL);
-
         TypeDeserializer td = delegateType.getTypeHandler();
         if (td == null) {
             td = ctxt.getConfig().findTypeDeserializer(delegateType);
         }
-        JsonDeserializer<Object> dd = findDeserializer(ctxt, delegateType, property);
+        // 04-May-2018, tatu: [databind#2021] check if there's custom deserializer attached
+        //    to type (resolved from parameter)
+        JsonDeserializer<Object> dd = delegateType.getValueHandler();
+        if (dd == null) {
+            dd = findDeserializer(ctxt, delegateType, property);
+        } else {
+            dd = (JsonDeserializer<Object>) ctxt.handleSecondaryContextualization(dd, property, delegateType);
+        }
         if (td != null) {
             td = td.forProperty(property);
             return new TypeWrappedDeserializer(td, dd);
@@ -628,7 +644,6 @@
         return dd;
     }
 
-
     /**
      * Helper method that can be used to see if specified property is annotated
      * to indicate use of a converter for property value (in case of container types,
@@ -636,7 +651,7 @@
      *<p>
      * NOTE: returned deserializer is NOT yet contextualized, caller needs to take
      * care to do that.
-     * 
+     *
      * @since 2.2
      */
     protected JsonDeserializer<Object> findConvertingDeserializer(DeserializationContext ctxt,
@@ -657,7 +672,7 @@
         }
         return null;
     }
-    
+
     /**
      * Although most of post-processing is done in resolve(), we only get
      * access to referring property's annotations here; and this is needed
@@ -673,9 +688,8 @@
 
         // First: may have an override for Object Id:
         final AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
-        final AnnotatedMember accessor = (property == null || intr == null)
-                ? null : property.getMember();
-        if (accessor != null && intr != null) {
+        final AnnotatedMember accessor = _neitherNull(property, intr) ? property.getMember() : null;
+        if (accessor != null) {
             ObjectIdInfo objectIdInfo = intr.findObjectIdInfo(accessor);
             if (objectIdInfo != null) { // some code duplication here as well (from BeanDeserializerFactory)
                 // 2.1: allow modifications by "id ref" annotations as well:
@@ -691,12 +705,13 @@
                     PropertyName propName = objectIdInfo.getPropertyName();
                     idProp = findProperty(propName);
                     if (idProp == null) {
-                        throw new IllegalArgumentException("Invalid Object Id definition for "
-                                +handledType().getName()+": can not find property with name '"+propName+"'");
+                        ctxt.reportBadDefinition(_beanType, String.format(
+                                "Invalid Object Id definition for %s: cannot find property with name '%s'",
+                                handledType().getName(), propName));
                     }
                     idType = idProp.getType();
                     idGen = new PropertyBasedObjectIdGenerator(objectIdInfo.getScope());
-                } else { // other types need to be simpler
+                } else { // other types are to be simpler
                     JavaType type = ctxt.constructType(implClass);
                     idType = ctxt.getTypeFactory().findTypeParameters(type, ObjectIdGenerator.class)[0];
                     idProp = null;
@@ -738,7 +753,6 @@
             // 16-May-2016, tatu: How about per-property case-insensitivity?
             Boolean B = format.getFeature(JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);
             if (B != null) {
-                // !!! TODO
                 BeanPropertyMap propsOrig = _beanProperties;
                 BeanPropertyMap props = propsOrig.withCaseInsensitivity(B.booleanValue());
                 if (props != propsOrig) {
@@ -762,6 +776,7 @@
      */
     protected SettableBeanProperty _resolveManagedReferenceProperty(DeserializationContext ctxt,
             SettableBeanProperty prop)
+        throws JsonMappingException
     {
         String refName = prop.getManagedReferenceName();
         if (refName == null) {
@@ -770,20 +785,21 @@
         JsonDeserializer<?> valueDeser = prop.getValueDeserializer();
         SettableBeanProperty backProp = valueDeser.findBackReference(refName);
         if (backProp == null) {
-            throw new IllegalArgumentException("Can not handle managed/back reference '"+refName+"': no back reference property found from type "
-                    +prop.getType());
+            ctxt.reportBadDefinition(_beanType, String.format(
+"Cannot handle managed/back reference '%s': no back reference property found from type %s",
+                    refName, prop.getType()));
         }
         // also: verify that type is compatible
         JavaType referredType = _beanType;
         JavaType backRefType = backProp.getType();
         boolean isContainer = prop.getType().isContainerType();
         if (!backRefType.getRawClass().isAssignableFrom(referredType.getRawClass())) {
-            throw new IllegalArgumentException("Can not handle managed/back reference '"+refName+"': back reference type ("
-                    +backRefType.getRawClass().getName()+") not compatible with managed type ("
-                    +referredType.getRawClass().getName()+")");
+            ctxt.reportBadDefinition(_beanType, String.format(
+"Cannot handle managed/back reference '%s': back reference type (%s) not compatible with managed type (%s)",
+                    refName, backRefType.getRawClass().getName(),
+                    referredType.getRawClass().getName()));
         }
-        return new ManagedReferenceProperty(prop, refName, backProp,
-                _classAnnotations, isContainer);
+        return new ManagedReferenceProperty(prop, refName, backProp, isContainer);
     }
 
     /**
@@ -806,24 +822,27 @@
      * Helper method called to see if given property might be so-called unwrapped
      * property: these require special handling.
      */
-    protected SettableBeanProperty _resolveUnwrappedProperty(DeserializationContext ctxt,
+    protected NameTransformer _findPropertyUnwrapper(DeserializationContext ctxt,
             SettableBeanProperty prop)
+        throws JsonMappingException
     {
         AnnotatedMember am = prop.getMember();
         if (am != null) {
             NameTransformer unwrapper = ctxt.getAnnotationIntrospector().findUnwrappingNameTransformer(am);
             if (unwrapper != null) {
-                JsonDeserializer<Object> orig = prop.getValueDeserializer();
-                JsonDeserializer<Object> unwrapping = orig.unwrappingDeserializer(unwrapper);
-                if (unwrapping != orig && unwrapping != null) {
-                    // might be cleaner to create new instance; but difficult to do reliably, so:
-                    return prop.withValueDeserializer(unwrapping);
+                // 01-Dec-2016, tatu: As per [databind#265] we cannot yet support passing
+                //   of unwrapped values through creator properties, so fail fast
+                if (prop instanceof CreatorProperty) {
+                    ctxt.reportBadDefinition(getValueType(), String.format(
+                            "Cannot define Creator property \"%s\" as `@JsonUnwrapped`: combination not yet supported",
+                            prop.getName()));
                 }
+                return unwrapper;
             }
         }
         return null;
     }
-    
+
     /**
      * Helper method that will handle gruesome details of dealing with properties
      * that have non-static inner class as value...
@@ -862,20 +881,96 @@
         return prop;
     }
 
+    // @since 2.9
+    protected SettableBeanProperty _resolveMergeAndNullSettings(DeserializationContext ctxt,
+            SettableBeanProperty prop, PropertyMetadata propMetadata)
+        throws JsonMappingException
+    {
+        PropertyMetadata.MergeInfo merge = propMetadata.getMergeInfo();
+        // First mergeability
+        if (merge != null) {
+            JsonDeserializer<?> valueDeser = prop.getValueDeserializer();
+            Boolean mayMerge = valueDeser.supportsUpdate(ctxt.getConfig());
+    
+            if (mayMerge == null) {
+                // we don't really know if it's ok; so only use if explicitly specified
+                if (merge.fromDefaults) {
+                    return prop;
+                }
+            } else if (!mayMerge.booleanValue()) { // prevented
+                if (!merge.fromDefaults) {
+                    // If attempts was made via explicit annotation/per-type config override,
+                    // should be reported; may or may not result in exception
+                    ctxt.reportBadMerge(valueDeser);
+                }
+                return prop;
+            }
+            // Anyway; if we get this far, do enable merging
+            AnnotatedMember accessor = merge.getter;
+            accessor.fixAccess(ctxt.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
+            if (!(prop instanceof SetterlessProperty)) {
+                prop = MergingSettableBeanProperty.construct(prop, accessor);
+            }
+        }
+
+        // And after this, see if we require non-standard null handling
+        NullValueProvider nuller = findValueNullProvider(ctxt, prop, propMetadata);
+        if (nuller != null) {
+            prop = prop.withNullProvider(nuller);
+        }
+        return prop;
+    }
+
     /*
     /**********************************************************
-    /* Public accessors
+    /* Public accessors; null/empty value providers
+    /**********************************************************
+     */
+
+    @Override
+    public AccessPattern getNullAccessPattern() {
+        // POJO types do not have custom `null` values
+        return AccessPattern.ALWAYS_NULL;
+    }
+
+    @Override
+    public AccessPattern getEmptyAccessPattern() {
+        // Empty values cannot be shared
+        return AccessPattern.DYNAMIC;
+    }
+    
+    @Override // since 2.9
+    public Object getEmptyValue(DeserializationContext ctxt) throws JsonMappingException {
+        // alas, need to promote exception, if any:
+        try {
+            return _valueInstantiator.createUsingDefault(ctxt);
+        } catch (IOException e) {
+            return ClassUtil.throwAsMappingException(ctxt, e);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Public accessors; other
     /**********************************************************
      */
 
     @Override
     public boolean isCachable() { return true; }
 
+    @Override // since 2.9
+    public Boolean supportsUpdate(DeserializationConfig config) {
+        // although with possible caveats, yes, values can be updated
+        // 23-Oct-2016, tatu: Perhaps in future could and should verify from
+        //   bean settings...
+        return Boolean.TRUE;
+    }
+    
     @Override
     public Class<?> handledType() {
         return _beanType.getRawClass();
     }
-    
+
     /**
      * Overridden to return true for those instances that are
      * handling value for which Object Identity handling is enabled
@@ -938,7 +1033,7 @@
      * Accessor for finding properties that represents values to pass
      * through property-based creator method (constructor or
      * factory method)
-     * 
+     *
      * @since 2.0
      */
     public Iterator<SettableBeanProperty> creatorProperties()
@@ -959,7 +1054,7 @@
      * Accessor for finding the property with given name, if POJO
      * has one. Name used is the external name, i.e. name used
      * in external data representation (JSON).
-     * 
+     *
      * @since 2.0
      */
     public SettableBeanProperty findProperty(String propertyName)
@@ -979,7 +1074,7 @@
      * since properties are not directly indexable; however, for most
      * instances difference is not significant as number of properties
      * is low.
-     * 
+     *
      * @since 2.3
      */
     public SettableBeanProperty findProperty(int propertyIndex)
@@ -1005,6 +1100,7 @@
         return _backRefs.get(logicalName);
     }
 
+    @Override // ValueInstantiator.Gettable
     public ValueInstantiator getValueInstantiator() {
         return _valueInstantiator;
     }
@@ -1024,13 +1120,13 @@
      *
      * @param original Property to replace
      * @param replacement Property to replace it with
-     * 
+     *
      * @since 2.1
      */
     public void replaceProperty(SettableBeanProperty original,
             SettableBeanProperty replacement)
     {
-        _beanProperties.replace(replacement);
+        _beanProperties.replace(original, replacement);
     }
 
     /*
@@ -1190,28 +1286,21 @@
         if (_propertyBasedCreator != null) {
             return _deserializeUsingPropertyBased(p, ctxt);
         }
-        // should only occur for abstract types...
-        if (_beanType.isAbstract()) {
-            return ctxt.handleMissingInstantiator(handledType(), p,
-                    "abstract type (need to add/enable type information?)");
-        }
         // 25-Jan-2017, tatu: We do not actually support use of Creators for non-static
         //   inner classes -- with one and only one exception; that of default constructor!
         //   -- so let's indicate it
         Class<?> raw = _beanType.getRawClass();
         if (ClassUtil.isNonStaticInnerClass(raw)) {
-            return ctxt.handleMissingInstantiator(raw, p,
+            return ctxt.handleMissingInstantiator(raw, null, p,
 "can only instantiate non-static inner class by using default, no-argument constructor");
         }
-        return ctxt.handleMissingInstantiator(raw, p,
-"no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?)");
+        return ctxt.handleMissingInstantiator(raw, getValueInstantiator(), p,
+                "cannot deserialize from Object value (no delegate- or property-based Creator)");
     }
 
     protected abstract Object _deserializeUsingPropertyBased(final JsonParser p,
-            final DeserializationContext ctxt)
-        throws IOException, JsonProcessingException;
+            final DeserializationContext ctxt) throws IOException;
 
-    @SuppressWarnings("incomplete-switch")
     public Object deserializeFromNumber(JsonParser p, DeserializationContext ctxt)
         throws IOException
     {
@@ -1220,8 +1309,8 @@
             return deserializeFromObjectId(p, ctxt);
         }
         final JsonDeserializer<Object> delegateDeser = _delegateDeserializer();
-        switch (p.getNumberType()) {
-        case INT:
+        NumberType nt = p.getNumberType();
+        if (nt == NumberType.INT) {
             if (delegateDeser != null) {
                 if (!_valueInstantiator.canCreateFromInt()) {
                     Object bean = _valueInstantiator.createUsingDelegate(ctxt,
@@ -1233,7 +1322,8 @@
                 }
             }
             return _valueInstantiator.createFromInt(ctxt, p.getIntValue());
-        case LONG:
+        }
+        if (nt == NumberType.LONG) {
             if (delegateDeser != null) {
                 if (!_valueInstantiator.canCreateFromInt()) {
                     Object bean = _valueInstantiator.createUsingDelegate(ctxt,
@@ -1255,12 +1345,13 @@
             }
             return bean;
         }
-        return ctxt.handleMissingInstantiator(handledType(), p,
+        return ctxt.handleMissingInstantiator(handledType(), getValueInstantiator(), p,
                 "no suitable creator method found to deserialize from Number value (%s)",
                 p.getNumberValue());
     }
 
-    public Object deserializeFromString(JsonParser p, DeserializationContext ctxt) throws IOException
+    public Object deserializeFromString(JsonParser p, DeserializationContext ctxt)
+        throws IOException
     {
         // First things first: id Object Id is used, most likely that's it
         if (_objectIdReader != null) {
@@ -1310,7 +1401,7 @@
             return _valueInstantiator.createUsingDelegate(ctxt,
                     delegateDeser.deserialize(p, ctxt));
         }
-        return ctxt.handleMissingInstantiator(handledType(), p,
+        return ctxt.handleMissingInstantiator(handledType(), getValueInstantiator(), p,
                 "no suitable creator method found to deserialize from Number value (%s)",
                 p.getNumberValue());
     }
@@ -1337,7 +1428,7 @@
 
     public Object deserializeFromArray(JsonParser p, DeserializationContext ctxt) throws IOException
     {
-        // note: can not call `_delegateDeserializer()` since order reversed here:
+        // note: cannot call `_delegateDeserializer()` since order reversed here:
         JsonDeserializer<Object> delegateDeser = _arrayDelegateDeserializer;
         // fallback to non-array delegate
         if ((delegateDeser != null) || ((delegateDeser = _delegateDeserializer) != null)) {
@@ -1390,10 +1481,19 @@
                 return bean;
             }
         }
-
         // TODO: maybe add support for ValueInstantiator, embedded?
-        
-        return p.getEmbeddedObject();
+
+        // 26-Jul-2017, tatu: related to [databind#1711], let's actually verify assignment
+        //    compatibility before returning. Bound to catch misconfigured cases and produce
+        //    more meaningful exceptions.
+        Object value = p.getEmbeddedObject();
+        if (value != null) {
+            if (!_beanType.isTypeOrSuperTypeOf(value.getClass())) {
+                // allow this to be handled...
+                value = ctxt.handleWeirdNativeValue(_beanType, value, p);
+            }
+        }
+        return value;
     }
 
     /**
@@ -1420,7 +1520,7 @@
             injector.inject(ctxt, bean);
         }
     }
-    
+
     /**
      * Method called to handle set of one or more unknown properties,
      * stored in their entirety in given {@link TokenBuffer}
@@ -1470,7 +1570,7 @@
 
     /**
      * Method called when a JSON property is encountered that has not matching
-     * setter, any-setter or field, and thus can not be assigned.
+     * setter, any-setter or field, and thus cannot be assigned.
      */
     @Override
     protected void handleUnknownProperty(JsonParser p, DeserializationContext ctxt,
@@ -1492,7 +1592,7 @@
     /**
      * Method called when an explicitly ignored property (one specified with a
      * name to match, either by property annotation or class annotation) is encountered.
-     * 
+     *
      * @since 2.3
      */
     protected void handleIgnoredProperty(JsonParser p, DeserializationContext ctxt,
@@ -1504,7 +1604,7 @@
         }
         p.skipChildren();
     }
-    
+
     /**
      * Method called in cases where we may have polymorphic deserialization
      * case: that is, type of Creator-constructed bean is not the type
@@ -1547,7 +1647,7 @@
         }
         return bean;
     }
-    
+
     /**
      * Helper method called to (try to) locate deserializer for given sub-type of
      * type that this deserializer handles.
@@ -1585,7 +1685,7 @@
         }
         return subDeser;
     }
-    
+
     /*
     /**********************************************************
     /* Helper methods for error reporting
@@ -1607,16 +1707,10 @@
     public void wrapAndThrow(Throwable t, Object bean, String fieldName, DeserializationContext ctxt)
         throws IOException
     {
-        // [JACKSON-55] Need to add reference information
+        // Need to add reference information
         throw JsonMappingException.wrapWithPath(throwOrReturnThrowable(t, ctxt), bean, fieldName);
     }
 
-    @Deprecated // since 2.4, not used by core Jackson; only relevant for arrays/Collections
-    public void wrapAndThrow(Throwable t, Object bean, int index, DeserializationContext ctxt) throws IOException {
-        // [JACKSON-55] Need to add reference information
-        throw JsonMappingException.wrapWithPath(throwOrReturnThrowable(t, ctxt), bean, index);
-    }
-
     private Throwable throwOrReturnThrowable(Throwable t, DeserializationContext ctxt) 
         throws IOException
     {
@@ -1628,9 +1722,7 @@
             t = t.getCause();
         }
         // Errors to be passed as is
-        if (t instanceof Error) {
-            throw (Error) t;
-        }
+        ClassUtil.throwIfError(t);
         boolean wrap = (ctxt == null) || ctxt.isEnabled(DeserializationFeature.WRAP_EXCEPTIONS);
         // Ditto for IOExceptions; except we may want to wrap JSON exceptions
         if (t instanceof IOException) {
@@ -1638,9 +1730,7 @@
                 throw (IOException) t;
             }
         } else if (!wrap) { // [JACKSON-407] -- allow disabling wrapping for unchecked exceptions
-            if (t instanceof RuntimeException) {
-                throw (RuntimeException) t;
-            }
+            ClassUtil.throwIfRTE(t);
         }
         return t;
     }
@@ -1652,17 +1742,14 @@
             t = t.getCause();
         }
         // Errors and "plain" IOExceptions to be passed as is
-        if (t instanceof Error) {
-            throw (Error) t;
-        }
-        boolean wrap = (ctxt == null) || ctxt.isEnabled(DeserializationFeature.WRAP_EXCEPTIONS);
+        ClassUtil.throwIfError(t);
         if (t instanceof IOException) {
             // Since we have no more information to add, let's not actually wrap..
             throw (IOException) t;
-        } else if (!wrap) { // [JACKSON-407] -- allow disabling wrapping for unchecked exceptions
-            if (t instanceof RuntimeException) {
-                throw (RuntimeException) t;
-            }
+        }
+        boolean wrap = (ctxt == null) || ctxt.isEnabled(DeserializationFeature.WRAP_EXCEPTIONS);
+        if (!wrap) { // [JACKSON-407] -- allow disabling wrapping for unchecked exceptions
+            ClassUtil.throwIfRTE(t);
         }
         return ctxt.handleInstantiationProblem(_beanType.getRawClass(), null, t);
     }
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBuilder.java b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBuilder.java
index 5ca54d5..c105307 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBuilder.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBuilder.java
@@ -26,6 +26,11 @@
 
     final protected DeserializationConfig _config;
 
+    /**
+     * @since 2.9
+     */
+    final protected DeserializationContext _context;
+
     /*
     /**********************************************************
     /* General information about POJO
@@ -42,18 +47,18 @@
     /* Accumulated information about properties
     /**********************************************************
      */
-    
+
     /**
      * Properties to deserialize collected so far.
      */
     final protected Map<String, SettableBeanProperty> _properties
         = new LinkedHashMap<String, SettableBeanProperty>();
-    
+
     /**
      * Value injectors for deserialization
      */
     protected List<ValueInjector> _injectables;
-    
+
     /**
      * Back-reference properties this bean contains (if any)
      */
@@ -64,7 +69,7 @@
      * purposes (meaning no exception is thrown, value is just skipped).
      */
     protected HashSet<String> _ignorableProps;
-    
+
     /**
      * Object that will handle value instantiation for the bean type.
      */
@@ -75,7 +80,7 @@
      * bean type.
      */
     protected ObjectIdReader _objectIdReader;
-    
+
     /**
      * Fallback setter used for handling any properties that are not
      * mapped to regular setters. If setter is not null, it will be
@@ -105,12 +110,13 @@
     /* Life-cycle: construction
     /**********************************************************
      */
-    
+
     public BeanDeserializerBuilder(BeanDescription beanDesc,
-            DeserializationConfig config)
+            DeserializationContext ctxt)
     { 
         _beanDesc = beanDesc;
-        _config = config;
+        _context = ctxt;
+        _config = ctxt.getConfig();
     }
 
     /**
@@ -120,6 +126,7 @@
     protected BeanDeserializerBuilder(BeanDeserializerBuilder src)
     {
         _beanDesc = src._beanDesc;
+        _context = src._context;
         _config = src._config;
 
         // let's make copy of properties
@@ -130,10 +137,10 @@
         _ignorableProps = src._ignorableProps;        
         _valueInstantiator = src._valueInstantiator;
         _objectIdReader = src._objectIdReader;
-        
+
         _anySetter = src._anySetter;
         _ignoreAllUnknown = src._ignoreAllUnknown;
-        
+
         _buildMethod = src._buildMethod;
         _builderConfig = src._builderConfig;
     }
@@ -146,7 +153,7 @@
     private static <T> List<T> _copy(List<T> src) {
         return (src == null) ? null : new ArrayList<T>(src);
     }
-    
+
     /*
     /**********************************************************
     /* Life-cycle: state modification (adders, setters)
@@ -188,12 +195,15 @@
         //    access set early; unfortunate, but since it works....
         prop.fixAccess(_config);
         _backRefProperties.put(referenceName, prop);
-        // also: if we had property with same name, actually remove it
+        // 16-Jan-2018, tatu: As per [databind#1878] we may want to leave it as is, to allow
+        //    population for cases of "wrong direction", traversing parent first
+        //   If this causes problems should probably instead include in "ignored properties" list
+        //   Alternatively could also extend annotation to allow/disallow explicit value from input
+        /*
         if (_properties != null) {
             _properties.remove(prop.getName());
         }
-        // ??? 23-Jul-2012, tatu: Should it be included in list of all properties?
-        //   For now, won't add, since it is inferred, not explicit...
+        */
     }
 
     public void addInjectable(PropertyName propName, JavaType propType,
@@ -208,8 +218,7 @@
         if (fixAccess) {
             member.fixAccess(forceAccess);
         }
-        _injectables.add(new ValueInjector(propName, propType,
-                contextAnnotations, member, valueId));
+        _injectables.add(new ValueInjector(propName, propType, member, valueId));
     }
 
     /**
@@ -318,6 +327,13 @@
         return _builderConfig;
     }
 
+    /**
+     * @since 2.9.4
+     */
+    public boolean hasIgnorable(String name) {
+        return (_ignorableProps != null) && _ignorableProps.contains(name);
+    }
+
     /*
     /**********************************************************
     /* Build method(s)
@@ -332,9 +348,9 @@
     {
         Collection<SettableBeanProperty> props = _properties.values();
         _fixAccess(props);
-
         BeanPropertyMap propertyMap = BeanPropertyMap.construct(props,
-                _config.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES));
+                _config.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES),
+                _collectAliases(props));
         propertyMap.assignIndexes();
 
         // view processing must be enabled if:
@@ -373,22 +389,22 @@
      * @since 2.0
      */
     public AbstractDeserializer buildAbstract() {
-        return new AbstractDeserializer(this, _beanDesc, _backRefProperties);
+        return new AbstractDeserializer(this, _beanDesc, _backRefProperties, _properties);
     }
 
     /**
      * Method for constructing a specialized deserializer that uses
      * additional external Builder object during data binding.
      */
-    public JsonDeserializer<?> buildBuilderBased(JavaType valueType,
-    		String expBuildMethodName)
+    public JsonDeserializer<?> buildBuilderBased(JavaType valueType, String expBuildMethodName)
+        throws JsonMappingException
     {
         // First: validation; must have build method that returns compatible type
         if (_buildMethod == null) {
             // as per [databind#777], allow empty name
             if (!expBuildMethodName.isEmpty()) {
-                throw new IllegalArgumentException(String.format(
-                        "Builder class %s does not have build method (name: '%s')",
+                _context.reportBadDefinition(_beanDesc.getType(),
+                        String.format("Builder class %s does not have build method (name: '%s')",
                         _beanDesc.getBeanClass().getName(),
                         expBuildMethodName));
             }
@@ -399,16 +415,19 @@
             if ((rawBuildType != rawValueType)
                     && !rawBuildType.isAssignableFrom(rawValueType)
                     && !rawValueType.isAssignableFrom(rawBuildType)) {
-                throw new IllegalArgumentException("Build method '"+_buildMethod.getFullName()
-                        +" has bad return type ("+rawBuildType.getName()
-                        +"), not compatible with POJO type ("+valueType.getRawClass().getName()+")");
+                _context.reportBadDefinition(_beanDesc.getType(),
+                        String.format("Build method '%s' has wrong return type (%s), not compatible with POJO type (%s)",
+                        _buildMethod.getFullName(),
+                        rawBuildType.getName(),
+                        valueType.getRawClass().getName()));
             }
         }
         // And if so, we can try building the deserializer
         Collection<SettableBeanProperty> props = _properties.values();
         _fixAccess(props);
         BeanPropertyMap propertyMap = BeanPropertyMap.construct(props,
-                _config.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES));
+                _config.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES),
+                _collectAliases(props));
         propertyMap.assignIndexes();
 
         boolean anyViews = !_config.isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION);
@@ -423,17 +442,15 @@
         }
 
         if (_objectIdReader != null) {
-            /* 18-Nov-2012, tatu: May or may not have annotations for id property;
-             *   but no easy access. But hard to see id property being optional,
-             *   so let's consider required at this point.
-             */
+            // May or may not have annotations for id property; but no easy access.
+            // But hard to see id property being optional, so let's consider required at this point.
             ObjectIdValueProperty prop = new ObjectIdValueProperty(_objectIdReader,
                     PropertyMetadata.STD_REQUIRED);
             propertyMap = propertyMap.withProperty(prop);
         }
 
         return new BuilderBasedDeserializer(this,
-                _beanDesc, propertyMap, _backRefProperties, _ignorableProps, _ignoreAllUnknown,
+                _beanDesc, valueType, propertyMap, _backRefProperties, _ignorableProps, _ignoreAllUnknown,
                 anyViews);
     }
 
@@ -443,7 +460,7 @@
     /**********************************************************
      */
 
-    private void _fixAccess(Collection<SettableBeanProperty> mainProps)
+    protected void _fixAccess(Collection<SettableBeanProperty> mainProps)
     {
         /* 07-Sep-2016, tatu: Ideally we should be able to avoid forcing
          *   access to properties that are likely ignored, but due to
@@ -482,4 +499,26 @@
             _buildMethod.fixAccess(_config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
         }
     }
+
+    protected Map<String,List<PropertyName>> _collectAliases(Collection<SettableBeanProperty> props)
+    {
+        Map<String,List<PropertyName>> mapping = null;
+        AnnotationIntrospector intr = _config.getAnnotationIntrospector();
+        if (intr != null) {
+            for (SettableBeanProperty prop : props) {
+                List<PropertyName> aliases = intr.findPropertyAliases(prop.getMember());
+                if ((aliases == null) || aliases.isEmpty()) {
+                    continue;
+                }
+                if (mapping == null) {
+                    mapping = new HashMap<>();
+                }
+                mapping.put(prop.getName(), aliases);
+            }
+        }
+        if (mapping == null) {
+            return Collections.emptyMap();
+        }
+        return mapping;
+    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java
index d93745c..1658f09 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java
@@ -3,13 +3,12 @@
 import java.util.*;
 
 import com.fasterxml.jackson.annotation.*;
-
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
 import com.fasterxml.jackson.databind.cfg.DeserializerFactoryConfig;
-import com.fasterxml.jackson.databind.cfg.ConfigOverride;
 import com.fasterxml.jackson.databind.deser.impl.*;
 import com.fasterxml.jackson.databind.deser.std.ThrowableDeserializer;
+import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
 import com.fasterxml.jackson.databind.introspect.*;
 import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
 import com.fasterxml.jackson.databind.jsontype.impl.SubTypeValidator;
@@ -38,8 +37,6 @@
      */
     private final static Class<?>[] INIT_CAUSE_PARAMS = new Class<?>[] { Throwable.class };
 
-    private final static Class<?>[] NO_VIEWS = new Class<?>[0];
-
     /*
     /**********************************************************
     /* Life-cycle
@@ -74,11 +71,7 @@
          *    Instead, let's actually just throw an error if this method is called when subtype
          *    has not properly overridden this method; this to indicate problem as soon as possible.
          */
-        if (getClass() != BeanDeserializerFactory.class) {
-            throw new IllegalStateException("Subtype of BeanDeserializerFactory ("+getClass().getName()
-                    +") has not properly overridden method 'withAdditionalDeserializers': can not instantiate subtype with "
-                    +"additional deserializer definitions");
-        }
+        ClassUtil.verifyMustOverride(BeanDeserializerFactory.class, this, "withConfig");
         return new BeanDeserializerFactory(config);
     }
     
@@ -111,9 +104,9 @@
         if (type.isThrowable()) {
             return buildThrowableDeserializer(ctxt, type, beanDesc);
         }
-        /* Or, for abstract types, may have alternate means for resolution
-         * (defaulting, materialization)
-         */
+        // Or, for abstract types, may have alternate means for resolution
+        // (defaulting, materialization)
+
         // 29-Nov-2015, tatu: Also, filter out calls to primitive types, they are
         //    not something we could materialize anything for
         if (type.isAbstract() && !type.isPrimitive() && !type.isEnumType()) {
@@ -145,17 +138,16 @@
     }
 
     @Override
-    public JsonDeserializer<Object> createBuilderBasedDeserializer(
-    		DeserializationContext ctxt, JavaType valueType, BeanDescription beanDesc,
-    		Class<?> builderClass)
-        throws JsonMappingException
+    public JsonDeserializer<Object> createBuilderBasedDeserializer(DeserializationContext ctxt,
+            JavaType valueType, BeanDescription beanDesc, Class<?> builderClass)
+                    throws JsonMappingException
     {
         // First: need a BeanDescription for builder class
         JavaType builderType = ctxt.constructType(builderClass);
         BeanDescription builderDesc = ctxt.getConfig().introspectForBuilder(builderType);
         return buildBuilderBasedDeserializer(ctxt, valueType, builderDesc);
     }
-    
+
     /**
      * Method called by {@link BeanDeserializerFactory} to see if there might be a standard
      * deserializer registered for given type.
@@ -222,6 +214,12 @@
             valueInstantiator = findValueInstantiator(ctxt, beanDesc);
         } catch (NoClassDefFoundError error) {
             return new ErrorThrowingDeserializer(error);
+        } catch (IllegalArgumentException e) {
+            // 05-Apr-2017, tatu: Although it might appear cleaner to require collector
+            //   to throw proper exception, it doesn't actually have reference to this
+            //   instance so...
+            throw InvalidDefinitionException.from(ctxt.getParser(), e.getMessage(),
+                    beanDesc, null);
         }
         BeanDeserializerBuilder builder = constructBeanDeserializerBuilder(ctxt, beanDesc);
         builder.setValueInstantiator(valueInstantiator);
@@ -230,7 +228,7 @@
         addObjectIdReader(ctxt, beanDesc, builder);
 
         // managed/back reference fields/setters need special handling... first part
-        addReferenceProperties(ctxt, beanDesc, builder);
+        addBackReferenceProperties(ctxt, beanDesc, builder);
         addInjectables(ctxt, beanDesc, builder);
         
         final DeserializationConfig config = ctxt.getConfig();
@@ -246,8 +244,8 @@
         } else {
             deserializer = builder.build();
         }
-
-        // [JACKSON-440]: may have modifier(s) that wants to modify or replace serializer we just built:
+        // may have modifier(s) that wants to modify or replace serializer we just built
+        // (note that `resolve()` and `createContextual()` called later on)
         if (_factoryConfig.hasDeserializerModifiers()) {
             for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) {
                 deserializer = mod.modifyDeserializer(config, beanDesc, deserializer);
@@ -268,8 +266,19 @@
     		DeserializationContext ctxt, JavaType valueType, BeanDescription builderDesc)
         throws JsonMappingException
     {
-    	// Creators, anyone? (to create builder itself)
-        ValueInstantiator valueInstantiator = findValueInstantiator(ctxt, builderDesc);
+        // Creators, anyone? (to create builder itself)
+        ValueInstantiator valueInstantiator;
+        try {
+            valueInstantiator = findValueInstantiator(ctxt, builderDesc);
+        } catch (NoClassDefFoundError error) {
+            return new ErrorThrowingDeserializer(error);
+        } catch (IllegalArgumentException e) {
+            // 05-Apr-2017, tatu: Although it might appear cleaner to require collector
+            //   to throw proper exception, it doesn't actually have reference to this
+            //   instance so...
+            throw InvalidDefinitionException.from(ctxt.getParser(), e.getMessage(),
+                    builderDesc, null);
+        }
         final DeserializationConfig config = ctxt.getConfig();
         BeanDeserializerBuilder builder = constructBeanDeserializerBuilder(ctxt, builderDesc);
         builder.setValueInstantiator(valueInstantiator);
@@ -278,12 +287,12 @@
         addObjectIdReader(ctxt, builderDesc, builder);
         
         // managed/back reference fields/setters need special handling... first part
-        addReferenceProperties(ctxt, builderDesc, builder);
+        addBackReferenceProperties(ctxt, builderDesc, builder);
         addInjectables(ctxt, builderDesc, builder);
 
         JsonPOJOBuilder.Value builderConfig = builderDesc.findPOJOBuilderConfig();
         final String buildMethodName = (builderConfig == null) ?
-                "build" : builderConfig.buildMethodName;
+                JsonPOJOBuilder.DEFAULT_BUILD_METHOD : builderConfig.buildMethodName;
         
         // and lastly, find build method to use:
         AnnotatedMethod buildMethod = builderDesc.findMethod(buildMethodName, null);
@@ -332,7 +341,7 @@
             idProp = builder.findProperty(propName);
             if (idProp == null) {
                 throw new IllegalArgumentException("Invalid Object Id definition for "
-                        +beanDesc.getBeanClass().getName()+": can not find property with name '"+propName+"'");
+                        +beanDesc.getBeanClass().getName()+": cannot find property with name '"+propName+"'");
             }
             idType = idProp.getType();
             gen = new PropertyBasedObjectIdGenerator(objectIdInfo.getScope());
@@ -362,9 +371,7 @@
         // (and assume there won't be any back references)
 
         // But then let's decorate things a bit
-        /* To resolve [JACKSON-95], need to add "initCause" as setter
-         * for exceptions (sub-classes of Throwable).
-         */
+        // Need to add "initCause" as setter for exceptions (sub-classes of Throwable).
         AnnotatedMethod am = beanDesc.findMethod("initCause", INIT_CAUSE_PARAMS);
         if (am != null) { // should never be null
             SimpleBeanPropertyDefinition propDef = SimpleBeanPropertyDefinition.construct(ctxt.getConfig(), am,
@@ -372,10 +379,8 @@
             SettableBeanProperty prop = constructSettableProperty(ctxt, beanDesc, propDef,
                     am.getParameterType(0));
             if (prop != null) {
-                /* 21-Aug-2011, tatus: We may actually have found 'cause' property
-                 *   to set... but let's replace it just in case,
-                 *   otherwise can end up with odd errors.
-                 */
+                // 21-Aug-2011, tatus: We may actually have found 'cause' property
+                //   to set... but let's replace it just in case, otherwise can end up with odd errors.
                 builder.addOrReplaceProperty(prop, true);
             }
         }
@@ -384,10 +389,10 @@
         builder.addIgnorable("localizedMessage");
         // Java 7 also added "getSuppressed", skip if we have such data:
         builder.addIgnorable("suppressed");
-        /* As well as "message": it will be passed via constructor,
-         * as there's no 'setMessage()' method
-        */
-        builder.addIgnorable("message");
+        // As well as "message": it will be passed via constructor,
+        // as there's no 'setMessage()' method
+        // 23-Jan-2018, tatu: ... although there MAY be Creator Property... which is problematic
+//        builder.addIgnorable("message");
 
         // update builder now that all information is in?
         if (_factoryConfig.hasDeserializerModifiers()) {
@@ -427,7 +432,7 @@
      */
     protected BeanDeserializerBuilder constructBeanDeserializerBuilder(DeserializationContext ctxt,
             BeanDescription beanDesc) {
-        return new BeanDeserializerBuilder(beanDesc, ctxt.getConfig());
+        return new BeanDeserializerBuilder(beanDesc, ctxt);
     }
     
     /**
@@ -446,7 +451,7 @@
                 ? builder.getValueInstantiator().getFromObjectArguments(ctxt.getConfig())
                 : null;
         final boolean hasCreatorProps = (creatorProps != null);
-        
+
         // 01-May-2016, tatu: Which base type to use here gets tricky, since
         //   it may often make most sense to use general type for overrides,
         //   but what we have here may be more specific impl type. But for now
@@ -455,7 +460,6 @@
                 .getDefaultPropertyIgnorals(beanDesc.getBeanClass(),
                         beanDesc.getClassInfo());
         Set<String> ignored;
-
         if (ignorals != null) {
             boolean ignoreAny = ignorals.getIgnoreUnknown();
             builder.setIgnoreUnknownProperties(ignoreAny);
@@ -469,20 +473,12 @@
         }
 
         // Also, do we have a fallback "any" setter?
-        AnnotatedMethod anySetterMethod = beanDesc.findAnySetter();
-        AnnotatedMember anySetterField = null;
-        if (anySetterMethod != null) {
-            builder.setAnySetter(constructAnySetter(ctxt, beanDesc, anySetterMethod));
-        }
-        else {
-        	anySetterField = beanDesc.findAnySetterField();
-        	if(anySetterField != null) {
-        		builder.setAnySetter(constructAnySetter(ctxt, beanDesc, anySetterField));
-        	}
-        }
-        // NOTE: we do NOT add @JsonIgnore'd properties into blocked ones if there's any-setter
-        // Implicit ones via @JsonIgnore and equivalent?
-        if (anySetterMethod == null && anySetterField == null) {
+        AnnotatedMember anySetter = beanDesc.findAnySetterAccessor();
+        if (anySetter != null) {
+            builder.setAnySetter(constructAnySetter(ctxt, beanDesc, anySetter));
+        } else {
+            // 23-Jan-2018, tatu: although [databind#1805] would suggest we should block
+            //   properties regardless, for now only consider unless there's any setter...
             Collection<String> ignored2 = beanDesc.getIgnoredPropertyNames();
             if (ignored2 != null) {
                 for (String propName : ignored2) {
@@ -498,39 +494,53 @@
         // Ok: let's then filter out property definitions
         List<BeanPropertyDefinition> propDefs = filterBeanProps(ctxt,
                 beanDesc, builder, beanDesc.findProperties(), ignored);
-
         // After which we can let custom code change the set
         if (_factoryConfig.hasDeserializerModifiers()) {
             for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) {
                 propDefs = mod.updateProperties(ctxt.getConfig(), beanDesc, propDefs);
             }
         }
-        
+
         // At which point we still have all kinds of properties; not all with mutators:
         for (BeanPropertyDefinition propDef : propDefs) {
             SettableBeanProperty prop = null;
-            /* 18-Oct-2013, tatu: Although constructor parameters have highest precedence,
-             *   we need to do linkage (as per [databind#318]), and so need to start with
-             *   other types, and only then create constructor parameter, if any.
-             */
+            
+            // 18-Oct-2013, tatu: Although constructor parameters have highest precedence,
+            //   we need to do linkage (as per [databind#318]), and so need to start with
+            //   other types, and only then create constructor parameter, if any.
             if (propDef.hasSetter()) {
-                JavaType propertyType = propDef.getSetter().getParameterType(0);
+                AnnotatedMethod setter = propDef.getSetter();
+                JavaType propertyType = setter.getParameterType(0);
                 prop = constructSettableProperty(ctxt, beanDesc, propDef, propertyType);
             } else if (propDef.hasField()) {
-                JavaType propertyType = propDef.getField().getType();
+                AnnotatedField field = propDef.getField();
+                JavaType propertyType = field.getType();
                 prop = constructSettableProperty(ctxt, beanDesc, propDef, propertyType);
-            } else if (useGettersAsSetters && propDef.hasGetter()) {
-                /* May also need to consider getters
-                 * for Map/Collection properties; but with lowest precedence
-                 */
+            } else {
+                // NOTE: specifically getter, since field was already checked above
                 AnnotatedMethod getter = propDef.getGetter();
-                // should only consider Collections and Maps, for now?
-                Class<?> rawPropertyType = getter.getRawType();
-                if (Collection.class.isAssignableFrom(rawPropertyType)
-                        || Map.class.isAssignableFrom(rawPropertyType)) {
-                    prop = constructSetterlessProperty(ctxt, beanDesc, propDef);
+                if (getter != null) {
+                    if (useGettersAsSetters && _isSetterlessType(getter.getRawType())) {
+                        // 23-Jan-2018, tatu: As per [databind#1805], need to ensure we don't
+                        //   accidentally sneak in getter-as-setter for `READ_ONLY` properties
+                        if (builder.hasIgnorable(propDef.getName())) {
+                            ;
+                        } else {
+                            prop = constructSetterlessProperty(ctxt, beanDesc, propDef);
+                        }
+                    } else if (!propDef.hasConstructorParameter()) {
+                        PropertyMetadata md = propDef.getMetadata();
+                        // 25-Oct-2016, tatu: If merging enabled, might not need setter.
+                        //   We cannot quite support this with creator parameters; in theory
+                        //   possibly, but right not not due to complexities of routing, so
+                        //   just prevent
+                        if (md.getMergeInfo() != null) {
+                            prop = constructSetterlessProperty(ctxt, beanDesc, propDef);
+                        }
+                    }
                 }
             }
+
             // 25-Sep-2014, tatu: No point in finding constructor parameters for abstract types
             //   (since they are never used anyway)
             if (hasCreatorProps && propDef.hasConstructorParameter()) {
@@ -562,26 +572,34 @@
                 if (prop != null) {
                     cprop.setFallbackSetter(prop);
                 }
-                prop = cprop;
+                Class<?>[] views = propDef.findViews();
+                if (views == null) {
+                    views = beanDesc.findDefaultViews();
+                }
+                cprop.setViews(views);
                 builder.addCreatorProperty(cprop);
                 continue;
             }
-
             if (prop != null) {
+                // one more thing before adding to builder: copy any metadata
                 Class<?>[] views = propDef.findViews();
                 if (views == null) {
-                    // one more twist: if default inclusion disabled, need to force empty set of views
-                    if (!ctxt.isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION)) {
-                        views = NO_VIEWS;
-                    }
+                    views = beanDesc.findDefaultViews();
                 }
-                // one more thing before adding to builder: copy any metadata
                 prop.setViews(views);
                 builder.addProperty(prop);
             }
         }
     }
-    
+
+    private boolean _isSetterlessType(Class<?> rawType) {
+        // May also need to consider getters
+        // for Map/Collection properties; but with lowest precedence
+        // should only consider Collections and Maps, for now?
+        return Collection.class.isAssignableFrom(rawType)
+                || Map.class.isAssignableFrom(rawType);
+    }
+
     /**
      * Helper method called to filter out explicit ignored properties,
      * as well as properties that have "ignorable types".
@@ -604,16 +622,10 @@
                 continue;
             }
             if (!property.hasConstructorParameter()) { // never skip constructor params
-                Class<?> rawPropertyType = null;
-                if (property.hasSetter()) {
-                    rawPropertyType = property.getSetter().getRawParameterType(0);
-                } else if (property.hasField()) {
-                    rawPropertyType = property.getField().getRawType();
-                }
-
+                Class<?> rawPropertyType = property.getRawPrimaryType();
                 // Some types are declared as ignorable as well
                 if ((rawPropertyType != null)
-                        && isIgnorableType(ctxt.getConfig(), beanDesc, rawPropertyType, ignoredTypes)) {
+                        && isIgnorableType(ctxt.getConfig(), property, rawPropertyType, ignoredTypes)) {
                     // important: make ignorable, to avoid errors if value is actually seen
                     builder.addIgnorable(name);
                     continue;
@@ -627,17 +639,19 @@
     /**
      * Method that will find if bean has any managed- or back-reference properties,
      * and if so add them to bean, to be linked during resolution phase.
+     *
+     * @since 2.9
      */
-    protected void addReferenceProperties(DeserializationContext ctxt,
+    protected void addBackReferenceProperties(DeserializationContext ctxt,
             BeanDescription beanDesc, BeanDeserializerBuilder builder)
         throws JsonMappingException
     {
         // and then back references, not necessarily found as regular properties
-        Map<String,AnnotatedMember> refs = beanDesc.findBackReferenceProperties();
-        if (refs != null) {
-            for (Map.Entry<String, AnnotatedMember> en : refs.entrySet()) {
-                String name = en.getKey();
-                AnnotatedMember m = en.getValue();
+        List<BeanPropertyDefinition> refProps = beanDesc.findBackReferences();
+        if (refProps != null) {
+            for (BeanPropertyDefinition refProp : refProps) {
+                /*
+                AnnotatedMember m = refProp.getMutator();
                 JavaType type;
                 if (m instanceof AnnotatedMethod) {
                     type = ((AnnotatedMethod) m).getParameterType(0);
@@ -647,18 +661,26 @@
                     //    work through constructors; but let's at least indicate the issue for now
                     if (m instanceof AnnotatedParameter) {
                         ctxt.reportBadTypeDefinition(beanDesc,
-"Can not bind back references as Creator parameters: type %s (reference '%s', parameter index #%d)",
-beanDesc.getBeanClass().getName(), name, ((AnnotatedParameter) m).getIndex());
+"Cannot bind back reference using Creator parameter (reference '%s', parameter index #%d)",
+name, ((AnnotatedParameter) m).getIndex());
                     }
                 }
-                SimpleBeanPropertyDefinition propDef = SimpleBeanPropertyDefinition.construct(
-                        ctxt.getConfig(), m, PropertyName.construct(name));
-                builder.addBackReferenceProperty(name, constructSettableProperty(ctxt,
-                        beanDesc, propDef, type));
+                */
+                String refName = refProp.findReferenceName();
+                builder.addBackReferenceProperty(refName, constructSettableProperty(ctxt,
+                        beanDesc, refProp, refProp.getPrimaryType()));
             }
         }
     }
 
+    @Deprecated // since 2.9 (rename)
+    protected void addReferenceProperties(DeserializationContext ctxt,
+            BeanDescription beanDesc, BeanDeserializerBuilder builder)
+        throws JsonMappingException
+    {
+        addBackReferenceProperties(ctxt, beanDesc, builder);
+    }
+
     /**
      * Method called locate all members used for value injection (if any),
      * constructor {@link com.fasterxml.jackson.databind.deser.impl.ValueInjector} instances, and add them to builder.
@@ -692,31 +714,58 @@
         throws JsonMappingException
     {
         //find the java type based on the annotated setter method or setter field 
-        JavaType type = null;
+        BeanProperty prop;
+        JavaType keyType;
+        JavaType valueType;
+
         if (mutator instanceof AnnotatedMethod) {
             // we know it's a 2-arg method, second arg is the value
-            type = ((AnnotatedMethod) mutator).getParameterType(1);
+            AnnotatedMethod am = (AnnotatedMethod) mutator;
+            keyType = am.getParameterType(0);
+            valueType = am.getParameterType(1);
+            valueType = resolveMemberAndTypeAnnotations(ctxt, mutator, valueType);
+            prop = new BeanProperty.Std(PropertyName.construct(mutator.getName()),
+                    valueType, null, mutator,
+                    PropertyMetadata.STD_OPTIONAL);
+
         } else if (mutator instanceof AnnotatedField) {
+            AnnotatedField af = (AnnotatedField) mutator;
             // get the type from the content type of the map object
-            type = ((AnnotatedField) mutator).getType().getContentType();
+            JavaType mapType = af.getType();
+            mapType = resolveMemberAndTypeAnnotations(ctxt, mutator, mapType);
+            keyType = mapType.getKeyType();
+            valueType = mapType.getContentType();
+            prop = new BeanProperty.Std(PropertyName.construct(mutator.getName()),
+                    mapType, null, mutator, PropertyMetadata.STD_OPTIONAL);
+        } else {
+            return ctxt.reportBadDefinition(beanDesc.getType(), String.format(
+                    "Unrecognized mutator type for any setter: %s", mutator.getClass()));
         }
-        // First: various annotations on type itself, as well as type-overrides
-        // on accessor need to be resolved
-        type = resolveMemberAndTypeAnnotations(ctxt, mutator, type);
-        BeanProperty.Std prop = new BeanProperty.Std(PropertyName.construct(mutator.getName()),
-                type, null, beanDesc.getClassAnnotations(), mutator,
-                PropertyMetadata.STD_OPTIONAL);
+        // First: see if there are explicitly specified 
         // and then possible direct deserializer override on accessor
-        JsonDeserializer<Object> deser = findDeserializerFromAnnotation(ctxt, mutator);
+        KeyDeserializer keyDeser = findKeyDeserializerFromAnnotation(ctxt, mutator);
+        if (keyDeser == null) {
+            keyDeser = keyType.getValueHandler();
+        }
+        if (keyDeser == null) {
+            keyDeser = ctxt.findKeyDeserializer(keyType, prop);
+        } else {
+            if (keyDeser instanceof ContextualKeyDeserializer) {
+                keyDeser = ((ContextualKeyDeserializer) keyDeser)
+                        .createContextual(ctxt, prop);
+            }
+        }
+        JsonDeserializer<Object> deser = findContentDeserializerFromAnnotation(ctxt, mutator);
         if (deser == null) {
-            deser = type.getValueHandler();
+            deser = valueType.getValueHandler();
         }
         if (deser != null) {
             // As per [databind#462] need to ensure we contextualize deserializer before passing it on
-            deser = (JsonDeserializer<Object>) ctxt.handlePrimaryContextualization(deser, prop, type);
+            deser = (JsonDeserializer<Object>) ctxt.handlePrimaryContextualization(deser, prop, valueType);
         }
-        TypeDeserializer typeDeser = type.getTypeHandler();
-        return new SettableAnyProperty(prop, mutator, type, deser, typeDeser);
+        TypeDeserializer typeDeser = valueType.getTypeHandler();
+        return new SettableAnyProperty(prop, mutator, valueType,
+                keyDeser, deser, typeDeser);
     }
 
     /**
@@ -803,7 +852,7 @@
 
     /**
      * Helper method used to skip processing for types that we know
-     * can not be (i.e. are never consider to be) beans: 
+     * cannot be (i.e. are never consider to be) beans: 
      * things like primitives, Arrays, Enums, and proxy types.
      *<p>
      * Note that usually we shouldn't really be getting these sort of
@@ -813,17 +862,17 @@
     {
         String typeStr = ClassUtil.canBeABeanType(type);
         if (typeStr != null) {
-            throw new IllegalArgumentException("Can not deserialize Class "+type.getName()+" (of type "+typeStr+") as a Bean");
+            throw new IllegalArgumentException("Cannot deserialize Class "+type.getName()+" (of type "+typeStr+") as a Bean");
         }
         if (ClassUtil.isProxyType(type)) {
-            throw new IllegalArgumentException("Can not deserialize Proxy class "+type.getName()+" as a Bean");
+            throw new IllegalArgumentException("Cannot deserialize Proxy class "+type.getName()+" as a Bean");
         }
         /* also: can't deserialize some local classes: static are ok; in-method not;
          * other non-static inner classes are ok
          */
         typeStr = ClassUtil.isLocalType(type, true);
         if (typeStr != null) {
-            throw new IllegalArgumentException("Can not deserialize Class "+type.getName()+" (of type "+typeStr+") as a Bean");
+            throw new IllegalArgumentException("Cannot deserialize Class "+type.getName()+" (of type "+typeStr+") as a Bean");
         }
         return true;
     }
@@ -832,24 +881,26 @@
      * Helper method that will check whether given raw type is marked as always ignorable
      * (for purpose of ignoring properties with type)
      */
-    protected boolean isIgnorableType(DeserializationConfig config, BeanDescription beanDesc,
+    protected boolean isIgnorableType(DeserializationConfig config, BeanPropertyDefinition propDef,
             Class<?> type, Map<Class<?>,Boolean> ignoredTypes)
     {
         Boolean status = ignoredTypes.get(type);
         if (status != null) {
             return status.booleanValue();
         }
-        // 21-Apr-2016, tatu: For 2.8, can specify config overrides
-        ConfigOverride override = config.findConfigOverride(type);
-        if (override != null) {
-            status = override.getIsIgnoredType();
-        }
-        if (status == null) {
-            BeanDescription desc = config.introspectClassAnnotations(type);
-            status = config.getAnnotationIntrospector().isIgnorableType(desc.getClassInfo());
-            // We default to 'false', i.e. not ignorable
+        // 22-Oct-2016, tatu: Slight check to skip primitives, String
+        if ((type == String.class) || type.isPrimitive()) {
+            status = Boolean.FALSE;
+        } else {
+            // 21-Apr-2016, tatu: For 2.8, can specify config overrides
+            status = config.getConfigOverride(type).getIsIgnoredType();
             if (status == null) {
-                status = Boolean.FALSE;
+                BeanDescription desc = config.introspectClassAnnotations(type);
+                status = config.getAnnotationIntrospector().isIgnorableType(desc.getClassInfo());
+                // We default to 'false', i.e. not ignorable
+                if (status == null) {
+                    status = Boolean.FALSE;
+                }
             }
         }
         ignoredTypes.put(type, status);
@@ -863,6 +914,6 @@
             BeanDescription beanDesc)
         throws JsonMappingException
     {
-        SubTypeValidator.instance().validateSubType(ctxt, type);
+        SubTypeValidator.instance().validateSubType(ctxt, type, beanDesc);
     }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BuilderBasedDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/BuilderBasedDeserializer.java
index 817e29e..4f7b345 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/BuilderBasedDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BuilderBasedDeserializer.java
@@ -27,6 +27,14 @@
 
     protected final AnnotatedMethod _buildMethod;
 
+    /**
+     * Type that the builder will produce, target type; as opposed to
+     * `handledType()` which refers to Builder class.
+     *
+     * @since 2.9
+     */
+    protected final JavaType _targetType;
+
     /*
     /**********************************************************
     /* Life-cycle, construction, initialization
@@ -37,22 +45,38 @@
      * Constructor used by {@link BeanDeserializerBuilder}.
      */
     public BuilderBasedDeserializer(BeanDeserializerBuilder builder,
-            BeanDescription beanDesc,
+            BeanDescription beanDesc, JavaType targetType,
             BeanPropertyMap properties, Map<String, SettableBeanProperty> backRefs,
             Set<String> ignorableProps, boolean ignoreAllUnknown,
             boolean hasViews)
     {
         super(builder, beanDesc, properties, backRefs,
                 ignorableProps, ignoreAllUnknown, hasViews);
+        _targetType = targetType;
         _buildMethod = builder.getBuildMethod();
-        // 05-Mar-2012, tatu: Can not really make Object Ids work with builders, not yet anyway
+        // 05-Mar-2012, tatu: Cannot really make Object Ids work with builders, not yet anyway
         if (_objectIdReader != null) {
-            throw new IllegalArgumentException("Can not use Object Id with Builder-based deserialization (type "
+            throw new IllegalArgumentException("Cannot use Object Id with Builder-based deserialization (type "
                     +beanDesc.getType()+")");
         }
     }
 
     /**
+     * @deprecated Since 2.9
+     */
+    @Deprecated
+    public BuilderBasedDeserializer(BeanDeserializerBuilder builder,
+            BeanDescription beanDesc,
+            BeanPropertyMap properties, Map<String, SettableBeanProperty> backRefs,
+            Set<String> ignorableProps, boolean ignoreAllUnknown,
+            boolean hasViews)
+    {
+        this(builder, beanDesc,
+                beanDesc.getType(), // Wrong! But got no access via `BeanDeserializerBuilder`
+                properties, backRefs, ignorableProps, ignoreAllUnknown, hasViews);
+    }
+
+    /**
      * Copy-constructor that can be used by sub-classes to allow
      * copy-on-write styling copying of settings of an existing instance.
      */
@@ -65,26 +89,31 @@
     {
         super(src, ignoreAllUnknown);
         _buildMethod = src._buildMethod;
+        _targetType = src._targetType;
     }
 
     protected BuilderBasedDeserializer(BuilderBasedDeserializer src, NameTransformer unwrapper) {
         super(src, unwrapper);
         _buildMethod = src._buildMethod;
+        _targetType = src._targetType;
     }
 
     public BuilderBasedDeserializer(BuilderBasedDeserializer src, ObjectIdReader oir) {
         super(src, oir);
         _buildMethod = src._buildMethod;
+        _targetType = src._targetType;
     }
 
     public BuilderBasedDeserializer(BuilderBasedDeserializer src, Set<String> ignorableProps) {
         super(src, ignorableProps);
         _buildMethod = src._buildMethod;
+        _targetType = src._targetType;
     }
 
     public BuilderBasedDeserializer(BuilderBasedDeserializer src, BeanPropertyMap props) {
         super(src, props);
         _buildMethod = src._buildMethod;
+        _targetType = src._targetType;
     }
 
     @Override
@@ -115,7 +144,7 @@
     @Override
     protected BeanDeserializerBase asArrayDeserializer() {
         SettableBeanProperty[] props = _beanProperties.getPropertiesInInsertionOrder();
-        return new BeanAsArrayBuilderDeserializer(this, props, _buildMethod);
+        return new BeanAsArrayBuilderDeserializer(this, _targetType, props, _buildMethod);
     }
 
     /*
@@ -124,7 +153,19 @@
     /**********************************************************
      */
 
-    protected final Object finishBuild(DeserializationContext ctxt, Object builder)
+    @Override // since 2.9
+    public Boolean supportsUpdate(DeserializationConfig config) {
+        // 26-Oct-2016, tatu: No, we can't merge Builder-based POJOs as of now
+        return Boolean.FALSE;
+    }
+    
+    /*
+    /**********************************************************
+    /* JsonDeserializer implementation
+    /**********************************************************
+     */
+
+    protected Object finishBuild(DeserializationContext ctxt, Object builder)
             throws IOException
     {
         // As per [databind#777], allow returning builder itself
@@ -132,7 +173,7 @@
             return builder;
         }
         try {
-            return _buildMethod.getMember().invoke(builder);
+            return _buildMethod.getMember().invoke(builder, (Object[]) null);
         } catch (Exception e) {
             return wrapInstantiationProblem(e, ctxt);
         }
@@ -142,42 +183,38 @@
      * Main deserialization method for bean-based objects (POJOs).
      */
     @Override
-    public final Object deserialize(JsonParser p, DeserializationContext ctxt)
+    public Object deserialize(JsonParser p, DeserializationContext ctxt)
         throws IOException
     {
-        JsonToken t = p.getCurrentToken();
-
         // common case first:
-        if (t == JsonToken.START_OBJECT) {
-            t = p.nextToken();
+        if (p.isExpectedStartObjectToken()) {
+            JsonToken t = p.nextToken();
             if (_vanillaProcessing) {
-            	return finishBuild(ctxt, vanillaDeserialize(p, ctxt, t));
+                return finishBuild(ctxt, vanillaDeserialize(p, ctxt, t));
             }
             Object builder = deserializeFromObject(p, ctxt);
             return finishBuild(ctxt, builder);
         }
         // and then others, generally requiring use of @JsonCreator
-        if (t != null) {
-            switch (t) {
-            case VALUE_STRING:
-                return finishBuild(ctxt, deserializeFromString(p, ctxt));
-            case VALUE_NUMBER_INT:
-                return finishBuild(ctxt, deserializeFromNumber(p, ctxt));
-            case VALUE_NUMBER_FLOAT:
-            	return finishBuild(ctxt, deserializeFromDouble(p, ctxt));
-            case VALUE_EMBEDDED_OBJECT:
-                return p.getEmbeddedObject();
-            case VALUE_TRUE:
-            case VALUE_FALSE:
-                return finishBuild(ctxt, deserializeFromBoolean(p, ctxt));
-            case START_ARRAY:
-                // these only work if there's a (delegating) creator...
-                return finishBuild(ctxt, deserializeFromArray(p, ctxt));
-            case FIELD_NAME:
-            case END_OBJECT:
-                return finishBuild(ctxt, deserializeFromObject(p, ctxt));
-            default:
-            }
+        switch (p.getCurrentTokenId()) {
+        case JsonTokenId.ID_STRING:
+            return finishBuild(ctxt, deserializeFromString(p, ctxt));
+        case JsonTokenId.ID_NUMBER_INT:
+            return finishBuild(ctxt, deserializeFromNumber(p, ctxt));
+        case JsonTokenId.ID_NUMBER_FLOAT:
+            return finishBuild(ctxt, deserializeFromDouble(p, ctxt));
+        case JsonTokenId.ID_EMBEDDED_OBJECT:
+            return p.getEmbeddedObject();
+        case JsonTokenId.ID_TRUE:
+        case JsonTokenId.ID_FALSE:
+            return finishBuild(ctxt, deserializeFromBoolean(p, ctxt));
+        case JsonTokenId.ID_START_ARRAY:
+            // these only work if there's a (delegating) creator...
+            return finishBuild(ctxt, deserializeFromArray(p, ctxt));
+        case JsonTokenId.ID_FIELD_NAME:
+        case JsonTokenId.ID_END_OBJECT:
+            return finishBuild(ctxt, deserializeFromObject(p, ctxt));
+        default:
         }
         return ctxt.handleUnexpectedToken(handledType(), p);
     }
@@ -189,13 +226,22 @@
      */
     @Override
     public Object deserialize(JsonParser p, DeserializationContext ctxt,
-    		Object builder)
-        throws IOException
+    		Object value) throws IOException
     {
-        /* Important: we call separate method which does NOT call
-         * 'finishBuild()', to avoid problems with recursion
-         */
-        return finishBuild(ctxt, _deserialize(p, ctxt, builder));
+        // 26-Oct-2016, tatu: I cannot see any of making this actually
+        //    work correctly, so let's indicate problem right away
+        JavaType valueType = _targetType;
+        // Did they try to give us builder?
+        Class<?> builderRawType = handledType();
+        Class<?> instRawType = value.getClass();
+        if (builderRawType.isAssignableFrom(instRawType)) {
+            return ctxt.reportBadDefinition(valueType, String.format(
+                    "Deserialization of %s by passing existing Builder (%s) instance not supported",
+                    valueType, builderRawType.getName()));
+        }
+        return ctxt.reportBadDefinition(valueType, String.format(
+                "Deserialization of %s by passing existing instance (of %s) not supported",
+                valueType, instRawType.getName()));
     }
 
     /*
@@ -204,59 +250,16 @@
     /**********************************************************
      */
 
-    protected final Object _deserialize(JsonParser p,
-            DeserializationContext ctxt, Object builder)
-        throws IOException, JsonProcessingException
-    {
-        if (_injectables != null) {
-            injectValues(ctxt, builder);
-        }
-        if (_unwrappedPropertyHandler != null) {
-            return deserializeWithUnwrapped(p, ctxt, builder);
-        }
-        if (_externalTypeIdHandler != null) {
-            return deserializeWithExternalTypeId(p, ctxt, builder);
-        }
-        if (_needViewProcesing) {
-            Class<?> view = ctxt.getActiveView();
-            if (view != null) {
-                return deserializeWithView(p, ctxt, builder, view);
-            }
-        }
-        JsonToken t = p.getCurrentToken();
-        // 23-Mar-2010, tatu: In some cases, we start with full JSON object too...
-        if (t == JsonToken.START_OBJECT) {
-            t = p.nextToken();
-        }
-        for (; t == JsonToken.FIELD_NAME; t = p.nextToken()) {
-            String propName = p.getCurrentName();
-            // Skip field name:
-            p.nextToken();
-            SettableBeanProperty prop = _beanProperties.find(propName);
-
-            if (prop != null) { // normal case
-                try {
-                    builder = prop.deserializeSetAndReturn(p, ctxt, builder);
-                } catch (Exception e) {
-                    wrapAndThrow(e, builder, propName, ctxt);
-                }
-                continue;
-            }
-            handleUnknownVanilla(p, ctxt, handledType(), propName);
-        }
-        return builder;
-    }
-
     /**
      * Streamlined version that is only used when no "special"
      * features are enabled.
      */
     private final Object vanillaDeserialize(JsonParser p,
     		DeserializationContext ctxt, JsonToken t)
-        throws IOException, JsonProcessingException
+        throws IOException
     {
         Object bean = _valueInstantiator.createUsingDefault(ctxt);
-        for (; p.getCurrentToken() != JsonToken.END_OBJECT; p.nextToken()) {
+        for (; p.getCurrentToken() == JsonToken.FIELD_NAME; p.nextToken()) {
             String propName = p.getCurrentName();
             // Skip field name:
             p.nextToken();
@@ -280,7 +283,7 @@
      */
     @Override
     public Object deserializeFromObject(JsonParser p, DeserializationContext ctxt)
-        throws IOException, JsonProcessingException
+        throws IOException
     {
         if (_nonStandardCreation) {
             if (_unwrappedPropertyHandler != null) {
@@ -301,7 +304,7 @@
                 return deserializeWithView(p, ctxt, bean, view);
             }
         }
-        for (; p.getCurrentToken() != JsonToken.END_OBJECT; p.nextToken()) {
+        for (; p.getCurrentToken() == JsonToken.FIELD_NAME; p.nextToken()) {
             String propName = p.getCurrentName();
             // Skip field name:
             p.nextToken();
@@ -326,15 +329,18 @@
      * values for creator method need to be buffered, first; and
      * due to non-guaranteed ordering possibly some other properties
      * as well.
+     *
+     * @return Builder instance constructed
      */
     @Override
     @SuppressWarnings("resource")
-    protected final Object _deserializeUsingPropertyBased(final JsonParser p,
+    protected Object _deserializeUsingPropertyBased(final JsonParser p,
             final DeserializationContext ctxt)
-        throws IOException, JsonProcessingException
-    {
+        throws IOException
+    { 
         final PropertyBasedCreator creator = _propertyBasedCreator;
         PropertyValueBuffer buffer = creator.startBuilding(p, ctxt, _objectIdReader);
+        final Class<?> activeView = _needViewProcesing ? ctxt.getActiveView() : null;
 
         // 04-Jan-2010, tatu: May need to collect unknown properties for polymorphic cases
         TokenBuffer unknown = null;
@@ -346,25 +352,29 @@
             // creator property?
             SettableBeanProperty creatorProp = creator.findCreatorProperty(propName);
             if (creatorProp != null) {
+                if ((activeView != null) && !creatorProp.visibleInView(activeView)) {
+                    p.skipChildren();
+                    continue;
+                }
                 // Last creator property to set?
                 if (buffer.assignParameter(creatorProp, creatorProp.deserialize(p, ctxt))) {
                     p.nextToken(); // to move to following FIELD_NAME/END_OBJECT
-                    Object bean;
+                    Object builder;
                     try {
-                        bean = creator.build(ctxt, buffer);
+                        builder = creator.build(ctxt, buffer);
                     } catch (Exception e) {
                         wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt);
                         continue; // never gets here
                     }
                     //  polymorphic?
-                    if (bean.getClass() != _beanType.getRawClass()) {
-                        return handlePolymorphic(p, ctxt, bean, unknown);
+                    if (builder.getClass() != _beanType.getRawClass()) {
+                        return handlePolymorphic(p, ctxt, builder, unknown);
                     }
                     if (unknown != null) { // nope, just extra unknown stuff...
-                        bean = handleUnknownProperties(ctxt, bean, unknown);
+                        builder = handleUnknownProperties(ctxt, builder, unknown);
                     }
                     // or just clean?
-                    return _deserialize(p, ctxt, bean);
+                    return _deserialize(p, ctxt, builder);
                 }
                 continue;
             }
@@ -398,21 +408,69 @@
         }
 
         // We hit END_OBJECT, so:
-        Object bean;
+        Object builder;
         try {
-            bean = creator.build(ctxt, buffer);
+            builder = creator.build(ctxt, buffer);
         } catch (Exception e) {
-            bean = wrapInstantiationProblem(e, ctxt);
+            builder = wrapInstantiationProblem(e, ctxt);
         }
         if (unknown != null) {
             // polymorphic?
-            if (bean.getClass() != _beanType.getRawClass()) {
-                return handlePolymorphic(null, ctxt, bean, unknown);
+            if (builder.getClass() != _beanType.getRawClass()) {
+                return handlePolymorphic(null, ctxt, builder, unknown);
             }
             // no, just some extra unknown properties
-            return handleUnknownProperties(ctxt, bean, unknown);
+            return handleUnknownProperties(ctxt, builder, unknown);
         }
-        return bean;
+        return builder;
+    }
+
+    @SuppressWarnings("resource")
+    protected final Object _deserialize(JsonParser p,
+            DeserializationContext ctxt, Object builder) throws IOException
+    {        
+        if (_injectables != null) {
+            injectValues(ctxt, builder);
+        }
+        if (_unwrappedPropertyHandler != null) {
+            if (p.hasToken(JsonToken.START_OBJECT)) {
+                p.nextToken();
+            }
+            TokenBuffer tokens = new TokenBuffer(p, ctxt);
+            tokens.writeStartObject();
+            return deserializeWithUnwrapped(p, ctxt, builder, tokens);
+        }
+        if (_externalTypeIdHandler != null) {
+            return deserializeWithExternalTypeId(p, ctxt, builder);
+        }
+        if (_needViewProcesing) {
+            Class<?> view = ctxt.getActiveView();
+            if (view != null) {
+                return deserializeWithView(p, ctxt, builder, view);
+            }
+        }
+        JsonToken t = p.getCurrentToken();
+        // 23-Mar-2010, tatu: In some cases, we start with full JSON object too...
+        if (t == JsonToken.START_OBJECT) {
+            t = p.nextToken();
+        }
+        for (; t == JsonToken.FIELD_NAME; t = p.nextToken()) {
+            String propName = p.getCurrentName();
+            // Skip field name:
+            p.nextToken();
+            SettableBeanProperty prop = _beanProperties.find(propName);
+            
+            if (prop != null) { // normal case
+                try {
+                    builder = prop.deserializeSetAndReturn(p, ctxt, builder);
+                } catch (Exception e) {
+                    wrapAndThrow(e, builder, propName, ctxt);
+                }
+                continue;
+            }
+            handleUnknownVanilla(p, ctxt, handledType(), propName);
+        }
+        return builder;
     }
 
     /*
@@ -423,7 +481,7 @@
 
     protected final Object deserializeWithView(JsonParser p, DeserializationContext ctxt,
             Object bean, Class<?> activeView)
-        throws IOException, JsonProcessingException
+        throws IOException
     {
         JsonToken t = p.getCurrentToken();
         for (; t == JsonToken.FIELD_NAME; t = p.nextToken()) {
@@ -460,7 +518,7 @@
      */
     @SuppressWarnings("resource")
     protected Object deserializeWithUnwrapped(JsonParser p, DeserializationContext ctxt)
-        throws IOException, JsonProcessingException
+        throws IOException
     {
         if (_delegateDeserializer != null) {
             return _valueInstantiator.createUsingDelegate(ctxt, _delegateDeserializer.deserialize(p, ctxt));
@@ -477,8 +535,7 @@
         }
 
         final Class<?> activeView = _needViewProcesing ? ctxt.getActiveView() : null;
-
-        for (; p.getCurrentToken() != JsonToken.END_OBJECT; p.nextToken()) {
+        for (; p.getCurrentToken() == JsonToken.FIELD_NAME; p.nextToken()) {
             String propName = p.getCurrentName();
             p.nextToken();
             SettableBeanProperty prop = _beanProperties.find(propName);
@@ -513,65 +570,20 @@
             }
         }
         tokens.writeEndObject();
-        _unwrappedPropertyHandler.processUnwrapped(p, ctxt, bean, tokens);
-        return bean;
-    }
-
-    @SuppressWarnings("resource")
-    protected Object deserializeWithUnwrapped(JsonParser p,
-    		DeserializationContext ctxt, Object bean)
-        throws IOException, JsonProcessingException
-    {
-        JsonToken t = p.getCurrentToken();
-        if (t == JsonToken.START_OBJECT) {
-            t = p.nextToken();
-        }
-        TokenBuffer tokens = new TokenBuffer(p, ctxt);
-        tokens.writeStartObject();
-        final Class<?> activeView = _needViewProcesing ? ctxt.getActiveView() : null;
-        for (; t == JsonToken.FIELD_NAME; t = p.nextToken()) {
-            String propName = p.getCurrentName();
-            SettableBeanProperty prop = _beanProperties.find(propName);
-            p.nextToken();
-            if (prop != null) { // normal case
-                if (activeView != null && !prop.visibleInView(activeView)) {
-                    p.skipChildren();
-                    continue;
-                }
-                try {
-                    bean = prop.deserializeSetAndReturn(p, ctxt, bean);
-                } catch (Exception e) {
-                    wrapAndThrow(e, bean, propName, ctxt);
-                }
-                continue;
-            }
-            if (_ignorableProps != null && _ignorableProps.contains(propName)) {
-                handleIgnoredProperty(p, ctxt, bean, propName);
-                continue;
-            }
-            // but... others should be passed to unwrapped property deserializers
-            tokens.writeFieldName(propName);
-            tokens.copyCurrentStructure(p);
-            // how about any setter? We'll get copies but...
-            if (_anySetter != null) {
-                _anySetter.deserializeAndSet(p, ctxt, bean, propName);
-            }
-        }
-        tokens.writeEndObject();
-        _unwrappedPropertyHandler.processUnwrapped(p, ctxt, bean, tokens);
-        return bean;
+        return _unwrappedPropertyHandler.processUnwrapped(p, ctxt, bean, tokens);
     }
 
     @SuppressWarnings("resource")
     protected Object deserializeUsingPropertyBasedWithUnwrapped(JsonParser p,
     		DeserializationContext ctxt)
-        throws IOException, JsonProcessingException
+        throws IOException
     {
         final PropertyBasedCreator creator = _propertyBasedCreator;
         PropertyValueBuffer buffer = creator.startBuilding(p, ctxt, _objectIdReader);
 
         TokenBuffer tokens = new TokenBuffer(p, ctxt);
         tokens.writeStartObject();
+        Object builder = null;
 
         JsonToken t = p.getCurrentToken();
         for (; t == JsonToken.FIELD_NAME; t = p.nextToken()) {
@@ -580,7 +592,20 @@
             // creator property?
             SettableBeanProperty creatorProp = creator.findCreatorProperty(propName);
             if (creatorProp != null) {
-                buffer.assignParameter(creatorProp, creatorProp.deserialize(p, ctxt));
+                // Last creator property to set?
+                if (buffer.assignParameter(creatorProp, creatorProp.deserialize(p, ctxt))) {
+                    t = p.nextToken(); // to move to following FIELD_NAME/END_OBJECT
+                    try {
+                        builder = creator.build(ctxt, buffer);
+                    } catch (Exception e) {
+                        wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt);
+                        continue; // never gets here
+                    }
+                    if (builder.getClass() != _beanType.getRawClass()) {
+                        return handlePolymorphic(p, ctxt, builder, tokens);
+                    }
+                    return deserializeWithUnwrapped(p, ctxt, builder, tokens);
+                }
                 continue;
             }
             // Object Id property?
@@ -604,16 +629,54 @@
                 buffer.bufferAnyProperty(_anySetter, propName, _anySetter.deserialize(p, ctxt));
             }
         }
+        tokens.writeEndObject();
 
         // We hit END_OBJECT, so:
-        Object bean;
-        // !!! 15-Feb-2012, tatu: Need to modify creator to use Builder!
-        try {
-            bean = creator.build(ctxt, buffer);
-        } catch (Exception e) {
-            return wrapInstantiationProblem(e, ctxt);
+        if (builder == null) {
+            try {
+                builder = creator.build(ctxt, buffer);
+            } catch (Exception e) {
+                return wrapInstantiationProblem(e, ctxt);
+            }
         }
-        return _unwrappedPropertyHandler.processUnwrapped(p, ctxt, bean, tokens);
+        return _unwrappedPropertyHandler.processUnwrapped(p, ctxt, builder, tokens);
+    }
+
+    protected Object deserializeWithUnwrapped(JsonParser p,
+            DeserializationContext ctxt, Object builder, TokenBuffer tokens)
+        throws IOException
+    {
+        final Class<?> activeView = _needViewProcesing ? ctxt.getActiveView() : null;
+        for (JsonToken t = p.getCurrentToken(); t == JsonToken.FIELD_NAME; t = p.nextToken()) {
+            String propName = p.getCurrentName();
+            SettableBeanProperty prop = _beanProperties.find(propName);
+            p.nextToken();
+            if (prop != null) { // normal case
+                if (activeView != null && !prop.visibleInView(activeView)) {
+                    p.skipChildren();
+                    continue;
+                }
+                try {
+                    builder = prop.deserializeSetAndReturn(p, ctxt, builder);
+                } catch (Exception e) {
+                    wrapAndThrow(e, builder, propName, ctxt);
+                }
+                continue;
+            }
+            if (_ignorableProps != null && _ignorableProps.contains(propName)) {
+                handleIgnoredProperty(p, ctxt, builder, propName);
+                continue;
+            }
+            // but... others should be passed to unwrapped property deserializers
+            tokens.writeFieldName(propName);
+            tokens.copyCurrentStructure(p);
+            // how about any setter? We'll get copies but...
+            if (_anySetter != null) {
+                _anySetter.deserializeAndSet(p, ctxt, builder, propName);
+            }
+        }
+        tokens.writeEndObject();
+        return _unwrappedPropertyHandler.processUnwrapped(p, ctxt, builder, tokens);
     }
 
     /*
@@ -624,7 +687,7 @@
      */
 
     protected Object deserializeWithExternalTypeId(JsonParser p, DeserializationContext ctxt)
-        throws IOException, JsonProcessingException
+        throws IOException
     {
         if (_propertyBasedCreator != null) {
             return deserializeUsingPropertyBasedWithExternalTypeId(p, ctxt);
@@ -634,7 +697,7 @@
 
     protected Object deserializeWithExternalTypeId(JsonParser p,
     		DeserializationContext ctxt, Object bean)
-        throws IOException, JsonProcessingException
+        throws IOException
     {
         final Class<?> activeView = _needViewProcesing ? ctxt.getActiveView() : null;
         final ExternalTypeHandler ext = _externalTypeIdHandler.start();
@@ -644,7 +707,7 @@
             t = p.nextToken();
             SettableBeanProperty prop = _beanProperties.find(propName);
             if (prop != null) { // normal case
-                // [JACKSON-831]: may have property AND be used as external type id:
+                // May have property AND be used as external type id:
                 if (t.isScalarValue()) {
                     ext.handleTypePropertyValue(p, ctxt, propName, bean);
                 }
@@ -675,6 +738,7 @@
                 } catch (Exception e) {
                     wrapAndThrow(e, bean, propName, ctxt);
                 }
+                continue;
             } else {
                 // Unknown: let's call handler method
                 handleUnknownProperty(p, ctxt, bean, propName);
@@ -686,9 +750,12 @@
 
     protected Object deserializeUsingPropertyBasedWithExternalTypeId(JsonParser p,
     		DeserializationContext ctxt)
-        throws IOException, JsonProcessingException
+        throws IOException
     {
         // !!! 04-Mar-2012, TODO: Need to fix -- will not work as is...
-        throw new IllegalStateException("Deserialization with Builder, External type id, @JsonCreator not yet implemented");
+        JavaType t = _targetType;
+        return ctxt.reportBadDefinition(t, String.format(
+                "Deserialization (of %s) with Builder, External type id, @JsonCreator not yet implemented",
+                t));
     }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/ContextualDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/ContextualDeserializer.java
index 11c972b..9db1f75 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/ContextualDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/ContextualDeserializer.java
@@ -29,7 +29,7 @@
      *    deserializers that may be needed by this deserializer
      * @param property Method, field or constructor parameter that represents the property
      *   (and is used to assign deserialized value).
-     *   Should be available; but there may be cases where caller can not provide it and
+     *   Should be available; but there may be cases where caller cannot provide it and
      *   null is passed instead (in which case impls usually pass 'this' deserializer as is)
      * 
      * @return Deserializer to use for deserializing values of specified property;
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/CreatorProperty.java b/src/main/java/com/fasterxml/jackson/databind/deser/CreatorProperty.java
index d8e8602..2d42a81 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/CreatorProperty.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/CreatorProperty.java
@@ -6,16 +6,19 @@
 import com.fasterxml.jackson.core.JsonParser;
 
 import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
 import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
 import com.fasterxml.jackson.databind.introspect.AnnotatedParameter;
 import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
 import com.fasterxml.jackson.databind.util.Annotations;
+import com.fasterxml.jackson.databind.util.ClassUtil;
 
 /**
  * This concrete sub-class implements property that is passed
  * via Creator (constructor or static factory method).
  * It is not a full-featured implementation in that its set method
- * should never be called -- instead, value must separately passed.
+ * should usually not be called for primary mutation -- instead, value must separately passed --
+ * but some aspects are still needed (specifically, injection).
  *<p>
  * Note on injectable values: unlike with other mutators, where
  * deserializer and injecting are separate, here we treat the two as related
@@ -41,12 +44,7 @@
     protected final Object _injectableValueId;
 
     /**
-     * @since 2.1
-     */
-    protected final int _creatorIndex;
-
-    /**
-     * In special cases, when implementing "updateValue", we can not use
+     * In special cases, when implementing "updateValue", we cannot use
      * constructors or factory methods, but have to fall back on using a
      * setter (or mutable field property). If so, this refers to that fallback
      * accessor.
@@ -59,6 +57,22 @@
     protected SettableBeanProperty _fallbackSetter;
 
     /**
+     * @since 2.1
+     */
+    protected final int _creatorIndex;
+
+    /**
+     * Marker flag that may have to be set during construction, to indicate that
+     * although property may have been constructed and added as a placeholder,
+     * it represents something that should be ignored during deserialization.
+     * This mostly concerns Creator properties which may not be easily deleted
+     * during processing.
+     *
+     * @since 2.9.4
+     */
+    protected boolean _ignorable;
+
+    /**
      * @param name Name of the logical property
      * @param type Type of the property, used to find deserializer
      * @param typeDeser Type deserializer to use for handling polymorphic type
@@ -91,33 +105,41 @@
     protected CreatorProperty(CreatorProperty src, PropertyName newName) {
         super(src, newName);
         _annotated = src._annotated;
-        _creatorIndex = src._creatorIndex;
         _injectableValueId = src._injectableValueId;
         _fallbackSetter = src._fallbackSetter;
+        _creatorIndex = src._creatorIndex;
+        _ignorable = src._ignorable;
     }
 
-    protected CreatorProperty(CreatorProperty src, JsonDeserializer<?> deser) {
-        super(src, deser);
+    protected CreatorProperty(CreatorProperty src, JsonDeserializer<?> deser,
+            NullValueProvider nva) {
+        super(src, deser, nva);
         _annotated = src._annotated;
-        _creatorIndex = src._creatorIndex;
         _injectableValueId = src._injectableValueId;
         _fallbackSetter = src._fallbackSetter;
+        _creatorIndex = src._creatorIndex;
+        _ignorable = src._ignorable;
     }
 
     @Override
-    public CreatorProperty withName(PropertyName newName) {
+    public SettableBeanProperty withName(PropertyName newName) {
         return new CreatorProperty(this, newName);
     }
     
     @Override
-    public CreatorProperty withValueDeserializer(JsonDeserializer<?> deser) {
+    public SettableBeanProperty withValueDeserializer(JsonDeserializer<?> deser) {
         if (_valueDeserializer == deser) {
             return this;
         }
-        return new CreatorProperty(this, deser);
+        return new CreatorProperty(this, deser, _nullProvider);
     }
 
     @Override
+    public SettableBeanProperty withNullProvider(NullValueProvider nva) {
+        return new CreatorProperty(this, _valueDeserializer, nva);
+    }
+    
+    @Override
     public void fixAccess(DeserializationConfig config) {
         if (_fallbackSetter != null) {
             _fallbackSetter.fixAccess(config);
@@ -134,19 +156,37 @@
         _fallbackSetter = fallbackSetter;
     }
 
+    @Override
+    public void markAsIgnorable() {
+        _ignorable = true;
+    }
+
+    @Override
+    public boolean isIgnorable() {
+        return _ignorable;
+    }
+
+    /*
+    /**********************************************************
+    /* Injection support
+    /**********************************************************
+     */
+
     /**
      * Method that can be called to locate value to be injected for this
      * property, if it is configured for this.
      */
     public Object findInjectableValue(DeserializationContext context, Object beanInstance)
+        throws JsonMappingException
     {
         if (_injectableValueId == null) {
-            throw new IllegalStateException("Property '"+getName()
-                    +"' (type "+getClass().getName()+") has no injectable value id configured");
+            context.reportBadDefinition(ClassUtil.classOf(beanInstance),
+                    String.format("Property '%s' (type %s) has no injectable value id configured",
+                    getName(), getClass().getName()));
         }
         return context.findInjectableValue(_injectableValueId, this, beanInstance);
     }
-    
+
     /**
      * Method to find value to inject, and inject it to this property.
      */
@@ -155,7 +195,7 @@
     {
         set(beanInstance, findInjectableValue(context, beanInstance));
     }
-    
+
     /*
     /**********************************************************
     /* BeanProperty impl
@@ -186,36 +226,29 @@
     public void deserializeAndSet(JsonParser p, DeserializationContext ctxt,
             Object instance) throws IOException
     {
-        set(instance, deserialize(p, ctxt));
+        _verifySetter();
+        _fallbackSetter.set(instance, deserialize(p, ctxt));
     }
 
     @Override
     public Object deserializeSetAndReturn(JsonParser p,
             DeserializationContext ctxt, Object instance) throws IOException
     {
-        return setAndReturn(instance, deserialize(p, ctxt));
+        _verifySetter();
+        return _fallbackSetter.setAndReturn(instance, deserialize(p, ctxt));
     }
     
     @Override
     public void set(Object instance, Object value) throws IOException
     {
-        /* Hmmmh. Should we return quietly (NOP), or error?
-         * Perhaps better to throw an exception, since it's generally an error.
-         */
-        if (_fallbackSetter == null) {
-            throw new IllegalStateException("No fallback setter/field defined: can not use creator property for "
-                    +getClass().getName());
-        }
+        _verifySetter();
         _fallbackSetter.set(instance, value);
     }
 
     @Override
     public Object setAndReturn(Object instance, Object value) throws IOException
     {
-        if (_fallbackSetter == null) {
-            throw new IllegalStateException("No fallback setter/field defined: can not use creator property for "
-                    +getClass().getName());
-        }
+        _verifySetter();
         return _fallbackSetter.setAndReturn(instance, value);
     }
     
@@ -226,4 +259,24 @@
 
     @Override
     public String toString() { return "[creator property, name '"+getName()+"'; inject id '"+_injectableValueId+"']"; }
+
+    // since 2.9
+    private final void _verifySetter() throws IOException {
+        if (_fallbackSetter == null) {
+            _reportMissingSetter(null, null);
+        }
+    }
+
+    // since 2.9
+    private void _reportMissingSetter(JsonParser p, DeserializationContext ctxt) throws IOException
+    {
+        final String msg = "No fallback setter/field defined for creator property '"+getName()+"'";
+        // Hmmmh. Should we return quietly (NOP), or error?
+        // Perhaps better to throw an exception, since it's generally an error.
+        if (ctxt != null ) {
+            ctxt.reportBadDefinition(getType(), msg);
+        } else {
+            throw InvalidDefinitionException.from(p, msg, getType());
+        }
+    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/DefaultDeserializationContext.java b/src/main/java/com/fasterxml/jackson/databind/deser/DefaultDeserializationContext.java
index d759ddd..cdc90ed 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/DefaultDeserializationContext.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/DefaultDeserializationContext.java
@@ -38,7 +38,7 @@
     /**
      * Constructor that will pass specified deserializer factory and
      * cache: cache may be null (in which case default implementation
-     * will be used), factory can not be null
+     * will be used), factory cannot be null
      */
     protected DefaultDeserializationContext(DeserializerFactory df, DeserializerCache cache) {
         super(df, cache);
@@ -331,16 +331,14 @@
 
         @Override
         public DefaultDeserializationContext copy() {
-            if (getClass() != Impl.class) {
-                return super.copy();
-            }
+            ClassUtil.verifyMustOverride(Impl.class, this, "copy");
            return new Impl(this);
         }
         
         @Override
         public DefaultDeserializationContext createInstance(DeserializationConfig config,
-                JsonParser jp, InjectableValues values) {
-            return new Impl(this, config, jp, values);
+                JsonParser p, InjectableValues values) {
+            return new Impl(this, config, p, values);
         }
 
         @Override
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/DeserializationProblemHandler.java b/src/main/java/com/fasterxml/jackson/databind/deser/DeserializationProblemHandler.java
index e374867..38b8705 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/DeserializationProblemHandler.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/DeserializationProblemHandler.java
@@ -75,7 +75,7 @@
     }
 
     /**
-     * Method called when a property name from input can not be converted to a
+     * Method called when a property name from input cannot be converted to a
      * non-Java-String key type (passed as <code>rawKeyType</code>) due to format problem.
      * Handler may choose to do one of 3 things:
      *<ul>
@@ -108,7 +108,7 @@
 
     /**
      * Method called when a String value
-     * can not be converted to a non-String value type due to specific problem
+     * cannot be converted to a non-String value type due to specific problem
      * (as opposed to String values never being usable).
      * Handler may choose to do one of 3 things:
      *<ul>
@@ -127,8 +127,8 @@
      *    to indicate type of failure unless handler produces key to use
      *
      * @return Either {@link #NOT_HANDLED} to indicate that handler does not know
-     *    what to do (and exception may be thrown), or value to use as key (possibly
-     *    <code>null</code>
+     *    what to do (and exception may be thrown), or value to use as (possibly
+     *    <code>null</code>)
      *
      * @since 2.8
      */
@@ -142,7 +142,7 @@
 
     /**
      * Method called when a numeric value (integral or floating-point from input
-     * can not be converted to a non-numeric value type due to specific problem
+     * cannot be converted to a non-numeric value type due to specific problem
      * (as opposed to numeric values never being usable).
      * Handler may choose to do one of 3 things:
      *<ul>
@@ -161,14 +161,41 @@
      *    to indicate type of failure unless handler produces key to use
      *
      * @return Either {@link #NOT_HANDLED} to indicate that handler does not know
-     *    what to do (and exception may be thrown), or value to use as key (possibly
-     *    <code>null</code>
+     *    what to do (and exception may be thrown), or value to use as (possibly
+     *    <code>null</code>)
      *
      * @since 2.8
      */
     public Object handleWeirdNumberValue(DeserializationContext ctxt,
-            Class<?> targetType, Number valueToConvert,
-            String failureMsg)
+            Class<?> targetType, Number valueToConvert, String failureMsg)
+        throws IOException
+    {
+        return NOT_HANDLED;
+    }
+
+    /**
+     * Method called when an embedded (native) value ({@link JsonToken#VALUE_EMBEDDED_OBJECT})
+     * cannot be converted directly into expected value type (usually POJO).
+     * Handler may choose to do one of 3 things:
+     *<ul>
+     * <li>Indicate it does not know what to do by returning {@link #NOT_HANDLED}
+     *  </li>
+     * <li>Throw a {@link IOException} to indicate specific fail message (instead of
+     *    standard exception caller would throw
+     *  </li>
+     * <li>Return actual converted value (of type <code>targetType</code>) to use as
+     *    replacement, and continue processing.
+     *  </li>
+     * </ul>
+     *
+     * @return Either {@link #NOT_HANDLED} to indicate that handler does not know
+     *    what to do (and exception may be thrown), or value to use (possibly
+     *    <code>null</code>)
+     *
+     * @since 2.9
+     */
+    public Object handleWeirdNativeValue(DeserializationContext ctxt,
+            JavaType targetType, Object valueToConvert, JsonParser p)
         throws IOException
     {
         return NOT_HANDLED;
@@ -177,7 +204,7 @@
     /**
      * Method that deserializers should call if the first token of the value to
      * deserialize is of unexpected type (that is, type of token that deserializer
-     * can not handle). This could occur, for example, if a Number deserializer
+     * cannot handle). This could occur, for example, if a Number deserializer
      * encounter {@link JsonToken#START_ARRAY} instead of
      * {@link JsonToken#VALUE_NUMBER_INT} or {@link JsonToken#VALUE_NUMBER_FLOAT}.
      *<ul>
@@ -267,15 +294,18 @@
      *    what to do (and exception may be thrown), or value to use (possibly
      *    <code>null</code>
      *
-     * @since 2.8
+     * @since 2.9
      */
     public Object handleMissingInstantiator(DeserializationContext ctxt,
-            Class<?> instClass, JsonParser p, String msg)
+            Class<?> instClass, ValueInstantiator valueInsta, JsonParser p,
+            String msg)
         throws IOException
     {
-        return NOT_HANDLED;
+        // 16-Oct-2016, tatu: Need to delegate to deprecated method from 2.8;
+        //   remove redirect from later versions (post-2.9)
+        return handleMissingInstantiator(ctxt, instClass, p, msg);
     }
-    
+
     /**
      * Handler method called if resolution of type id from given String failed
      * to produce a subtype; usually because logical id is not mapped to actual
@@ -313,4 +343,58 @@
     {
         return null;
     }
+
+    /**
+     * Handler method called if an expected type id for a polymorphic value is
+     * not found and no "default type" is specified or allowed.
+     * Handler may choose to do one of following things:
+     *<ul>
+     * <li>Indicate it does not know what to do by returning `null`
+     *  </li>
+     * <li>Indicate that nothing should be deserialized, by return `Void.class`
+     *  </li>
+     * <li>Throw a {@link IOException} to indicate specific fail message (instead of
+     *    standard exception caller would throw
+     *  </li>
+     * <li>Return actual resolved type to use for this particular case.
+     *  </li>
+     * </ul>
+     *
+     * @param ctxt Deserialization context to use for accessing information or
+     *    constructing exception to throw
+     * @param baseType Base type to use for resolving subtype id
+     * @param failureMsg Informational message that would be thrown as part of
+     *    exception, if resolution still fails
+     *
+     * @return Actual type to use, if resolved; `null` if handler does not know what
+     *     to do; or `Void.class` to indicate that nothing should be deserialized for
+     *     type with the id (which caller may choose to do... or not)
+     *
+     * @since 2.9
+     */
+    public JavaType handleMissingTypeId(DeserializationContext ctxt,
+            JavaType baseType, TypeIdResolver idResolver,
+            String failureMsg)
+        throws IOException
+    {
+        return null;
+    }
+
+    /*
+    /**********************************************************
+    /* Deprecated
+    /**********************************************************
+     */
+
+    /**
+     * @since 2.8
+     * @deprecated Since 2.9: use variant that takes {@link ValueInstantiator}
+     */
+    @Deprecated
+    public Object handleMissingInstantiator(DeserializationContext ctxt,
+            Class<?> instClass, JsonParser p, String msg)
+        throws IOException
+    {
+        return NOT_HANDLED;
+    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/DeserializerCache.java b/src/main/java/com/fasterxml/jackson/databind/deser/DeserializerCache.java
index ded6b68..b4dbdbe 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/DeserializerCache.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/DeserializerCache.java
@@ -372,11 +372,19 @@
                 return factory.createArrayDeserializer(ctxt, (ArrayType) type, beanDesc);
             }
             if (type.isMapLikeType()) {
-                MapLikeType mlt = (MapLikeType) type;
-                if (mlt.isTrueMapType()) {
-                    return factory.createMapDeserializer(ctxt,(MapType) mlt, beanDesc);
+                // 11-Mar-2017, tatu: As per [databind#1554], also need to block
+                //    handling as Map if overriden with "as POJO" option.
+                // Ideally we'd determine it bit later on (to allow custom handler checks)
+                // but that won't work for other reasons. So do it here.
+                // (read: rewrite for 3.0)
+                JsonFormat.Value format = beanDesc.findExpectedFormat(null);
+                if ((format == null) || format.getShape() != JsonFormat.Shape.OBJECT) {
+                    MapLikeType mlt = (MapLikeType) type;
+                    if (mlt.isTrueMapType()) {
+                        return factory.createMapDeserializer(ctxt,(MapType) mlt, beanDesc);
+                    }
+                    return factory.createMapLikeDeserializer(ctxt, mlt, beanDesc);
                 }
-                return factory.createMapLikeDeserializer(ctxt, mlt, beanDesc);
             }
             if (type.isCollectionLikeType()) {
                 /* 03-Aug-2012, tatu: As per [databind#40], one exception is if shape
@@ -574,23 +582,20 @@
     /**********************************************************
      */
 
-    // NOTE: changed 2.6 -> 2.7 to pass context; no way to make backwards compatible
     protected JsonDeserializer<Object> _handleUnknownValueDeserializer(DeserializationContext ctxt, JavaType type)
         throws JsonMappingException
     {
         // Let's try to figure out the reason, to give better error messages
         Class<?> rawClass = type.getRawClass();
         if (!ClassUtil.isConcrete(rawClass)) {
-            ctxt.reportMappingException("Can not find a Value deserializer for abstract type %s", type);
+            return ctxt.reportBadDefinition(type, "Cannot find a Value deserializer for abstract type "+type);
         }
-        ctxt.reportMappingException("Can not find a Value deserializer for type %s", type);
-        return null;
+        return ctxt.reportBadDefinition(type, "Cannot find a Value deserializer for type "+type);
     }
 
     protected KeyDeserializer _handleUnknownKeyDeserializer(DeserializationContext ctxt, JavaType type)
         throws JsonMappingException
     {
-        ctxt.reportMappingException("Can not find a (Map) Key deserializer for type %s", type);
-        return null;
+        return ctxt.reportBadDefinition(type, "Cannot find a (Map) Key deserializer for type "+type);
     }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/DeserializerFactory.java b/src/main/java/com/fasterxml/jackson/databind/deser/DeserializerFactory.java
index ffba4e4..d0638520 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/DeserializerFactory.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/DeserializerFactory.java
@@ -184,7 +184,7 @@
     public abstract KeyDeserializer createKeyDeserializer(DeserializationContext ctxt,
             JavaType type)
         throws JsonMappingException;
-    
+
     /**
      * Method called to find and create a type information deserializer for given base type,
      * if one is needed. If not needed (no polymorphic handling configured for type),
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/NullValueProvider.java b/src/main/java/com/fasterxml/jackson/databind/deser/NullValueProvider.java
new file mode 100644
index 0000000..b62834d
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/NullValueProvider.java
@@ -0,0 +1,34 @@
+package com.fasterxml.jackson.databind.deser;
+
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.util.AccessPattern;
+
+/**
+ * Helper interface implemented by classes that are to be used as
+ * null providers during deserialization. Most importantly implemented by
+ * {@link com.fasterxml.jackson.databind.JsonDeserializer} (as a mix-in
+ * interface), but also by converters used to support more configurable
+ * null replacement.
+ *
+ * @since 2.9
+ */
+public interface NullValueProvider
+{
+    /**
+     * Method called to possibly convert incoming `null` token (read via
+     * underlying streaming input source) into other value of type accessor
+     * supports. May return `null`, or value compatible with type binding.
+     *<p>
+     * NOTE: if {@link #getNullAccessPattern()} returns `ALWAYS_NULL` or
+     * `CONSTANT`, this method WILL NOT use provided `ctxt` and it may thus
+     * be passed as `null`.
+     */
+    public Object getNullValue(DeserializationContext ctxt) throws JsonMappingException;
+
+    /**
+     * Accessor that may be used to determine if and when provider must be called to
+     * access null replacement value.
+     */
+    public AccessPattern getNullAccessPattern(); 
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/SettableAnyProperty.java b/src/main/java/com/fasterxml/jackson/databind/deser/SettableAnyProperty.java
index 8ee86d4..e6663f1 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/SettableAnyProperty.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/SettableAnyProperty.java
@@ -10,6 +10,7 @@
 import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
 import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
 import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.util.ClassUtil;
 
 /**
  * Class that represents a "wildcard" set method which can be used
@@ -42,7 +43,12 @@
     protected JsonDeserializer<Object> _valueDeserializer;
 
     protected final TypeDeserializer _valueTypeDeserializer;
-    
+
+    /**
+     * @since 2.9
+     */
+    protected final KeyDeserializer _keyDeserializer;
+
     /*
     /**********************************************************
     /* Life-cycle
@@ -50,6 +56,7 @@
      */
 
     public SettableAnyProperty(BeanProperty property, AnnotatedMember setter, JavaType type,
+            KeyDeserializer keyDeser,
             JsonDeserializer<Object> valueDeser, TypeDeserializer typeDeser)
     {
         _property = property;
@@ -57,25 +64,20 @@
         _type = type;
         _valueDeserializer = valueDeser;
         _valueTypeDeserializer = typeDeser;
+        _keyDeserializer = keyDeser;
         _setterIsField = setter instanceof AnnotatedField;
     }
 
-    /**
-     * Constructor used for JDK Serialization when reading persisted object
-     */
-    protected SettableAnyProperty(SettableAnyProperty src)
+    @Deprecated // since 2.9
+    public SettableAnyProperty(BeanProperty property, AnnotatedMember setter, JavaType type,
+            JsonDeserializer<Object> valueDeser, TypeDeserializer typeDeser)
     {
-        _property = src._property;
-        _setter = src._setter;
-        _type = src._type;
-        _valueDeserializer = src._valueDeserializer;
-        _valueTypeDeserializer = src._valueTypeDeserializer;
-        _setterIsField = src._setterIsField;
+        this(property, setter, type, null, valueDeser, typeDeser);
     }
 
     public SettableAnyProperty withValueDeserializer(JsonDeserializer<Object> deser) {
         return new SettableAnyProperty(_property, _setter, _type,
-                deser, _valueTypeDeserializer);
+                _keyDeserializer, deser, _valueTypeDeserializer);
     }
 
     public void fixAccess(DeserializationConfig config) {
@@ -127,7 +129,9 @@
         throws IOException
     {
         try {
-            set(instance, propName, deserialize(p, ctxt));
+            Object key = (_keyDeserializer == null) ? propName
+                    : _keyDeserializer.deserializeKey(propName, ctxt);
+            set(instance, key, deserialize(p, ctxt));
         } catch (UnresolvedForwardReference reference) {
             if (!(_valueDeserializer.getObjectIdReader() != null)) {
                 throw JsonMappingException.from(p, "Unresolved forward reference but no identity info.", reference);
@@ -151,7 +155,7 @@
     }
 
     @SuppressWarnings("unchecked")
-    public void set(Object instance, String propName, Object value) throws IOException
+    public void set(Object instance, Object propName, Object value) throws IOException
     {
         try {
             // if annotation in the field (only map is supported now)
@@ -159,7 +163,7 @@
                 AnnotatedField field = (AnnotatedField) _setter;
                 Map<Object,Object> val = (Map<Object,Object>) field.getValue(instance);
                 /* 01-Jun-2016, tatu: At this point it is not quite clear what to do if
-                 *    field is `null` -- we can not necessarily count on zero-args
+                 *    field is `null` -- we cannot necessarily count on zero-args
                  *    constructor except for a small set of types, so for now just
                  *    ignore if null. May need to figure out something better in future.
                  */
@@ -168,7 +172,7 @@
                     val.put(propName, value);
                 }
             } else {
-                // note: can not use 'setValue()' due to taking 2 args
+                // note: cannot use 'setValue()' due to taking 2 args
                 ((AnnotatedMethod) _setter).callOnWith(instance, propName, value);
             }
         } catch (Exception e) {
@@ -187,11 +191,11 @@
      * @param propName Name of property (from Json input) to set
      * @param value Value of the property
      */
-    protected void _throwAsIOE(Exception e, String propName, Object value)
+    protected void _throwAsIOE(Exception e, Object propName, Object value)
         throws IOException
     {
         if (e instanceof IllegalArgumentException) {
-            String actType = (value == null) ? "[NULL]" : value.getClass().getName();
+            String actType = ClassUtil.classNameOf(value);
             StringBuilder msg = new StringBuilder("Problem deserializing \"any\" property '").append(propName);
             msg.append("' of class "+getClassName()+" (expected type: ").append(_type);
             msg.append("; actual type: ").append(actType).append(")");
@@ -203,17 +207,10 @@
             }
             throw new JsonMappingException(null, msg.toString(), e);
         }
-        if (e instanceof IOException) {
-            throw (IOException) e;
-        }
-        if (e instanceof RuntimeException) {
-            throw (RuntimeException) e;
-        }
+        ClassUtil.throwIfIOE(e);
+        ClassUtil.throwIfRTE(e);
         // let's wrap the innermost problem
-        Throwable t = e;
-        while (t.getCause() != null) {
-            t = t.getCause();
-        }
+        Throwable t = ClassUtil.getRootCause(e);
         throw new JsonMappingException(null, t.getMessage(), t);
     }
 
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/SettableBeanProperty.java b/src/main/java/com/fasterxml/jackson/databind/deser/SettableBeanProperty.java
index 65be982..31a5bb0 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/SettableBeanProperty.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/SettableBeanProperty.java
@@ -7,10 +7,12 @@
 
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.deser.impl.FailingDeserializer;
+import com.fasterxml.jackson.databind.deser.impl.NullsConstantProvider;
 import com.fasterxml.jackson.databind.introspect.*;
 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor;
 import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
 import com.fasterxml.jackson.databind.util.Annotations;
+import com.fasterxml.jackson.databind.util.ClassUtil;
 import com.fasterxml.jackson.databind.util.ViewMatcher;
 
 /**
@@ -33,7 +35,7 @@
      */
     protected static final JsonDeserializer<Object> MISSING_VALUE_DESERIALIZER = new FailingDeserializer(
             "No _valueDeserializer assigned");
-    
+
     /**
      * Logical name of the property (often but not always derived
      * from the setter method name)
@@ -49,14 +51,14 @@
      * @since 2.2
      */
     protected final PropertyName _wrapperName;
-    
+
     /**
      * Class that contains this property (either class that declares
      * the property or one of its subclasses), class that is
      * deserialized using deserializer that contains this property.
      */
     protected final transient Annotations _contextAnnotations;
-    
+
     /**
      * Deserializer used for handling property value.
      *<p>
@@ -71,18 +73,26 @@
      */
     protected final TypeDeserializer _valueTypeDeserializer;
 
+    /**
+     * Entity used for possible translation from `null` into non-null
+     * value of type of this property.
+     * Often same as <code>_valueDeserializer</code>, but not always.
+     *
+     * @since 2.9
+     */
+    protected final NullValueProvider _nullProvider;
+
     /*
     /**********************************************************
     /* Configuration that is not yet immutable; generally assigned
-    /* during initialization process but can not be passed to
+    /* during initialization process but cannot be passed to
     /* constructor.
     /**********************************************************
      */
 
     /**
-     * If property represents a managed (forward) reference
-     * (see [JACKSON-235]), we will need name of reference for
-     * later linking.
+     * If property represents a managed (forward) reference, we will need
+     * the name of reference for later linking.
      *<p>
      * TODO: should try to make immutable.
      */
@@ -103,7 +113,7 @@
      * TODO: should try to make immutable.
      */
     protected ViewMatcher _viewMatcher;
-    
+
     /**
      * Index of property (within all property of a bean); assigned
      * when all properties have been collected. Order of entries
@@ -127,15 +137,6 @@
                 contextAnnotations, propDef.getMetadata());
     }
 
-    @Deprecated // since 2.3
-    protected SettableBeanProperty(String propName, JavaType type, PropertyName wrapper,
-            TypeDeserializer typeDeser, Annotations contextAnnotations,
-            boolean isRequired)
-    {
-        this(new PropertyName(propName), type, wrapper, typeDeser, contextAnnotations,
-                PropertyMetadata.construct(Boolean.valueOf(isRequired), null, null, null));
-    }
-    
     protected SettableBeanProperty(PropertyName propName, JavaType type, PropertyName wrapper,
             TypeDeserializer typeDeser, Annotations contextAnnotations,
             PropertyMetadata metadata)
@@ -162,6 +163,7 @@
         }
         _valueTypeDeserializer = typeDeser;
         _valueDeserializer = MISSING_VALUE_DESERIALIZER;
+        _nullProvider = MISSING_VALUE_DESERIALIZER;
     }
 
     /**
@@ -185,8 +187,10 @@
         _viewMatcher = null;
         _valueTypeDeserializer = null;
         _valueDeserializer = valueDeser;
+        // 29-Jan-2017, tatu: Presumed to be irrelevant for ObjectId values...
+        _nullProvider = valueDeser;
     }
-    
+
     /**
      * Basic copy-constructor for sub-classes to use.
      */
@@ -202,13 +206,15 @@
         _managedReferenceName = src._managedReferenceName;
         _propertyIndex = src._propertyIndex;
         _viewMatcher = src._viewMatcher;
+        _nullProvider = src._nullProvider;
     }
 
     /**
      * Copy-with-deserializer-change constructor for sub-classes to use.
      */
     @SuppressWarnings("unchecked")
-    protected SettableBeanProperty(SettableBeanProperty src, JsonDeserializer<?> deser)
+    protected SettableBeanProperty(SettableBeanProperty src,
+            JsonDeserializer<?> deser, NullValueProvider nuller)
     {
         super(src);
         _propName = src._propName;
@@ -225,6 +231,11 @@
             _valueDeserializer = (JsonDeserializer<Object>) deser;
         }
         _viewMatcher = src._viewMatcher;
+        // 29-Jan-2017, tatu: Bit messy, but for now has to do...
+        if (nuller == MISSING_VALUE_DESERIALIZER) {
+            nuller = _valueDeserializer;
+        }
+        _nullProvider = nuller;
     }
 
     /**
@@ -242,6 +253,7 @@
         _managedReferenceName = src._managedReferenceName;
         _propertyIndex = src._propertyIndex;
         _viewMatcher = src._viewMatcher;
+        _nullProvider = src._nullProvider;
     }
 
     /**
@@ -276,11 +288,11 @@
                 ? new PropertyName(simpleName) : _propName.withSimpleName(simpleName);
         return (n == _propName) ? this : withName(n);
     }
-    
-    @Deprecated // since 2.3 -- use 'withSimpleName' instead if need be
-    public SettableBeanProperty withName(String simpleName) {
-        return withName(new PropertyName(simpleName));
-    }
+
+    /**
+     * @since 2.9
+     */
+    public abstract SettableBeanProperty withNullProvider(NullValueProvider nva);
 
     public void setManagedReferenceName(String n) {
         _managedReferenceName = n;
@@ -297,7 +309,7 @@
             _viewMatcher = ViewMatcher.construct(views);
         }
     }
-    
+
     /**
      * Method used to assign index for property.
      */
@@ -319,6 +331,16 @@
         ;
     }
 
+    /**
+     * @since 2.9.4
+     */
+    public void markAsIgnorable() { }
+
+    /**
+     * @since 2.9.4
+     */
+    public boolean isIgnorable() { return false; }
+
     /*
     /**********************************************************
     /* BeanProperty impl
@@ -372,7 +394,7 @@
     /**********************************************************
      */
 
-    protected final Class<?> getDeclaringClass() {
+    protected Class<?> getDeclaringClass() {
         return getMember().getDeclaringClass();
     }
 
@@ -385,7 +407,7 @@
     }
 
     public boolean hasValueTypeDeserializer() { return (_valueTypeDeserializer != null); }
-    
+
     public JsonDeserializer<Object> getValueDeserializer() {
         JsonDeserializer<Object> deser = _valueDeserializer;
         if (deser == MISSING_VALUE_DESERIALIZER) {
@@ -396,10 +418,15 @@
 
     public TypeDeserializer getValueTypeDeserializer() { return _valueTypeDeserializer; }
 
+    /**
+     * @since 2.9
+     */
+    public NullValueProvider getNullValueProvider() { return _nullProvider; }
+
     public boolean visibleInView(Class<?> activeView) {
         return (_viewMatcher == null) || _viewMatcher.isVisibleForView(activeView);
     }
-    
+
     public boolean hasViews() { return _viewMatcher != null; }
     
     /**
@@ -493,15 +520,51 @@
      */
     public final Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
     {
-        JsonToken t = p.getCurrentToken();
-
-        if (t == JsonToken.VALUE_NULL) {
-            return _valueDeserializer.getNullValue(ctxt);
+        if (p.hasToken(JsonToken.VALUE_NULL)) {
+            return _nullProvider.getNullValue(ctxt);
         }
         if (_valueTypeDeserializer != null) {
             return _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer);
         }
-        return _valueDeserializer.deserialize(p, ctxt);
+        // 04-May-2018, tatu: [databind#2023] Coercion from String (mostly) can give null
+        Object value =  _valueDeserializer.deserialize(p, ctxt);
+        if (value == null) {
+            value = _nullProvider.getNullValue(ctxt);
+        }
+        return value;
+    }
+
+    /**
+     * @since 2.9
+     */
+    public final Object deserializeWith(JsonParser p, DeserializationContext ctxt,
+            Object toUpdate) throws IOException
+    {
+        // 20-Oct-2016, tatu: Not 100% sure what to do; probably best to simply return
+        //   null value and let caller decide what to do.
+        if (p.hasToken(JsonToken.VALUE_NULL)) {
+            // ... except for "skip nulls" case which should just do that:
+            if (NullsConstantProvider.isSkipper(_nullProvider)) {
+                return toUpdate;
+            }
+            return _nullProvider.getNullValue(ctxt);
+        }
+        // 20-Oct-2016, tatu: Also tricky -- for now, report an error
+        if (_valueTypeDeserializer != null) {
+            ctxt.reportBadDefinition(getType(),
+                    String.format("Cannot merge polymorphic property '%s'",
+                            getName()));
+//            return _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer);
+        }
+        // 04-May-2018, tatu: [databind#2023] Coercion from String (mostly) can give null
+        Object value = _valueDeserializer.deserialize(p, ctxt, toUpdate);
+        if (value == null) {
+            if (NullsConstantProvider.isSkipper(_nullProvider)) {
+                return toUpdate;
+            }
+            value = _nullProvider.getNullValue(ctxt);
+        }
+        return value;
     }
 
     /*
@@ -517,13 +580,17 @@
     protected void _throwAsIOE(JsonParser p, Exception e, Object value) throws IOException
     {
         if (e instanceof IllegalArgumentException) {
-            String actType = (value == null) ? "[NULL]" : value.getClass().getName();
-            StringBuilder msg = new StringBuilder("Problem deserializing property '").append(getName());
-            msg.append("' (expected type: ").append(getType());
-            msg.append("; actual type: ").append(actType).append(")");
+            String actType = ClassUtil.classNameOf(value);
+            StringBuilder msg = new StringBuilder("Problem deserializing property '")
+                    .append(getName())
+                    .append("' (expected type: ")
+                    .append(getType())
+                    .append("; actual type: ")
+                    .append(actType).append(")");
             String origMsg = e.getMessage();
             if (origMsg != null) {
-                msg.append(", problem: ").append(origMsg);
+                msg.append(", problem: ")
+                    .append(origMsg);
             } else {
                 msg.append(" (no error message provided)");
             }
@@ -537,17 +604,10 @@
      */
     protected IOException _throwAsIOE(JsonParser p, Exception e) throws IOException
     {
-        if (e instanceof IOException) {
-            throw (IOException) e;
-        }
-        if (e instanceof RuntimeException) {
-            throw (RuntimeException) e;
-        }
+        ClassUtil.throwIfIOE(e);
+        ClassUtil.throwIfRTE(e);
         // let's wrap the innermost problem
-        Throwable th = e;
-        while (th.getCause() != null) {
-            th = th.getCause();
-        }
+        Throwable th = ClassUtil.getRootCause(e);
         throw JsonMappingException.from(p, th.getMessage(), th);
     }
 
@@ -557,10 +617,166 @@
     }
 
     // 10-Oct-2015, tatu: _Should_ be deprecated, too, but its remaining
-    //   callers can not actually provide a JsonParser
+    //   callers cannot actually provide a JsonParser
     protected void _throwAsIOE(Exception e, Object value) throws IOException {
         _throwAsIOE((JsonParser) null, e, value);
     }
 
     @Override public String toString() { return "[property '"+getName()+"']"; }
+
+    /*
+    /**********************************************************
+    /* Helper classes
+    /**********************************************************
+     */
+
+    /**
+     * Helper class that is designed to both make it easier to sub-class
+     * delegating subtypes and to reduce likelihood of breakage when
+     * new methods are added.
+     *<p>
+     * Class was specifically added to help with {@code Afterburner}
+     * module, but its use is not limited to only support it.
+     *
+     * @since 2.9
+     */
+    public static abstract class Delegating
+        extends SettableBeanProperty
+    {
+        protected final SettableBeanProperty delegate;
+
+        protected Delegating(SettableBeanProperty d) {
+            super(d);
+            delegate = d;
+        }
+
+        /**
+         * Method sub-classes must implement, to construct a new instance
+         * with given delegate.
+         */
+        protected abstract SettableBeanProperty withDelegate(SettableBeanProperty d);
+
+        protected SettableBeanProperty _with(SettableBeanProperty newDelegate) {
+            if (newDelegate == delegate) {
+                return this;
+            }
+            return withDelegate(newDelegate);
+        }
+        
+        @Override
+        public SettableBeanProperty withValueDeserializer(JsonDeserializer<?> deser) {
+            return _with(delegate.withValueDeserializer(deser));
+        }
+
+        @Override
+        public SettableBeanProperty withName(PropertyName newName) {
+            return _with(delegate.withName(newName));
+        }
+
+        @Override
+        public SettableBeanProperty withNullProvider(NullValueProvider nva) {
+            return _with(delegate.withNullProvider(nva));
+        }
+
+        @Override
+        public void assignIndex(int index) {
+            delegate.assignIndex(index);
+        }
+
+        @Override
+        public void fixAccess(DeserializationConfig config) {
+            delegate.fixAccess(config);
+        }
+
+        /*
+        /**********************************************************
+        /* Accessors
+        /**********************************************************
+         */
+
+        @Override
+        protected Class<?> getDeclaringClass() { return delegate.getDeclaringClass(); }
+
+        @Override
+        public String getManagedReferenceName() { return delegate.getManagedReferenceName(); }
+
+        @Override
+        public ObjectIdInfo getObjectIdInfo() { return delegate.getObjectIdInfo(); }
+
+        @Override
+        public boolean hasValueDeserializer() { return delegate.hasValueDeserializer(); }
+
+        @Override
+        public boolean hasValueTypeDeserializer() { return delegate.hasValueTypeDeserializer(); }
+        
+        @Override
+        public JsonDeserializer<Object> getValueDeserializer() { return delegate.getValueDeserializer(); }
+
+        @Override
+        public TypeDeserializer getValueTypeDeserializer() { return delegate.getValueTypeDeserializer(); }
+
+        @Override
+        public boolean visibleInView(Class<?> activeView) { return delegate.visibleInView(activeView); }
+
+        @Override
+        public boolean hasViews() { return delegate.hasViews(); }
+
+        @Override
+        public int getPropertyIndex() { return delegate.getPropertyIndex(); }
+
+        @Override
+        public int getCreatorIndex() { return delegate.getCreatorIndex(); }
+
+        @Override
+        public Object getInjectableValueId() { return delegate.getInjectableValueId(); }
+
+        @Override
+        public AnnotatedMember getMember() {
+            return delegate.getMember();
+        }
+
+        @Override
+        public <A extends Annotation> A getAnnotation(Class<A> acls) {
+            return delegate.getAnnotation(acls);
+        }
+
+        /*
+        /**********************************************************
+        /* Extended API
+        /**********************************************************
+         */
+
+        public SettableBeanProperty getDelegate() {
+            return delegate;
+        }
+
+        /*
+        /**********************************************************
+        /* Actual mutators
+        /**********************************************************
+         */
+
+        @Override
+        public void deserializeAndSet(JsonParser p, DeserializationContext ctxt,
+                Object instance) throws IOException {
+            delegate.deserializeAndSet(p, ctxt, instance);
+        }
+
+        @Override
+        public Object deserializeSetAndReturn(JsonParser p,
+                DeserializationContext ctxt, Object instance) throws IOException
+        {
+            return delegate.deserializeSetAndReturn(p, ctxt, instance);
+        }
+
+        @Override
+        public void set(Object instance, Object value) throws IOException {
+            delegate.set(instance, value);
+        }
+
+        @Override
+        public Object setAndReturn(Object instance, Object value) throws IOException {
+            return delegate.setAndReturn(instance, value);
+        }
+    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/UnresolvedId.java b/src/main/java/com/fasterxml/jackson/databind/deser/UnresolvedId.java
index c828616..af5772a 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/UnresolvedId.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/UnresolvedId.java
@@ -1,6 +1,7 @@
 package com.fasterxml.jackson.databind.deser;
 
 import com.fasterxml.jackson.core.JsonLocation;
+import com.fasterxml.jackson.databind.util.ClassUtil;
 
 /**
  * Helper class for {@link UnresolvedForwardReference}, to contain information about unresolved ids.
@@ -32,6 +33,6 @@
     @Override
     public String toString() {
         return String.format("Object id [%s] (for %s) at %s", _id,
-                (_type == null) ? "NULL" : _type.getName(), _location);
+                ClassUtil.nameOf(_type), _location);
     }
-}
\ No newline at end of file
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/ValueInstantiator.java b/src/main/java/com/fasterxml/jackson/databind/deser/ValueInstantiator.java
index fceac96..a7a6951 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/ValueInstantiator.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/ValueInstantiator.java
@@ -18,7 +18,7 @@
  * is a scalar value (String, number, boolean).
  *<p>
  * Note that this type is not parameterized (even though it would seemingly
- * make sense), because such type information can not be use effectively
+ * make sense), because such type information cannot be use effectively
  * during runtime: access is always using either wildcard type, or just
  * basic {@link java.lang.Object}; and so adding type parameter seems
  * like unnecessary extra work.
@@ -186,7 +186,7 @@
      * null or empty List.
      */
     public Object createUsingDefault(DeserializationContext ctxt) throws IOException {
-        return ctxt.handleMissingInstantiator(getValueClass(), ctxt.getParser(),
+        return ctxt.handleMissingInstantiator(getValueClass(), this, null,
                 "no default no-arguments constructor found");
     }
 
@@ -200,7 +200,7 @@
      */
     public Object createFromObjectWith(DeserializationContext ctxt, Object[] args) throws IOException {
         // sanity check; shouldn't really get called if no Creator specified
-        return ctxt.handleMissingInstantiator(getValueClass(), ctxt.getParser(),
+        return ctxt.handleMissingInstantiator(getValueClass(), this, null,
                 "no creator with arguments specified");
     }
 
@@ -234,7 +234,7 @@
      * an intermediate "delegate" value to pass to createor method
      */
     public Object createUsingDelegate(DeserializationContext ctxt, Object delegate) throws IOException {
-        return ctxt.handleMissingInstantiator(getValueClass(), ctxt.getParser(),
+        return ctxt.handleMissingInstantiator(getValueClass(), this, null,
                 "no delegate creator specified");
     }
 
@@ -243,7 +243,7 @@
      * an intermediate "delegate" value to pass to createor method
      */
     public Object createUsingArrayDelegate(DeserializationContext ctxt, Object delegate) throws IOException {
-        return ctxt.handleMissingInstantiator(getValueClass(), ctxt.getParser(),
+        return ctxt.handleMissingInstantiator(getValueClass(), this, null,
                 "no array delegate creator specified");
     }
 
@@ -259,25 +259,25 @@
     }
 
     public Object createFromInt(DeserializationContext ctxt, int value) throws IOException {
-        return ctxt.handleMissingInstantiator(getValueClass(), ctxt.getParser(),
+        return ctxt.handleMissingInstantiator(getValueClass(), this, null,
                 "no int/Int-argument constructor/factory method to deserialize from Number value (%s)",
                 value);
     }
 
     public Object createFromLong(DeserializationContext ctxt, long value) throws IOException {
-        return ctxt.handleMissingInstantiator(getValueClass(), ctxt.getParser(),
+        return ctxt.handleMissingInstantiator(getValueClass(), this, null,
                 "no long/Long-argument constructor/factory method to deserialize from Number value (%s)",
                 value);
     }
 
     public Object createFromDouble(DeserializationContext ctxt, double value) throws IOException {
-        return ctxt.handleMissingInstantiator(getValueClass(), ctxt.getParser(),
+        return ctxt.handleMissingInstantiator(getValueClass(), this, null,
                 "no double/Double-argument constructor/factory method to deserialize from Number value (%s)",
                 value);
     }
 
     public Object createFromBoolean(DeserializationContext ctxt, boolean value) throws IOException {
-        return ctxt.handleMissingInstantiator(getValueClass(), ctxt.getParser(),
+        return ctxt.handleMissingInstantiator(getValueClass(), this, null,
                 "no boolean/Boolean-argument constructor/factory method to deserialize from boolean value (%s)",
                 value);
     }
@@ -368,13 +368,26 @@
                 return null;
             }
         }
-        return ctxt.handleMissingInstantiator(getValueClass(), ctxt.getParser(),
+        return ctxt.handleMissingInstantiator(getValueClass(), this, ctxt.getParser(),
                 "no String-argument constructor/factory method to deserialize from String value ('%s')",
                 value);
     }
 
     /*
     /**********************************************************
+    /* Introspection
+    /**********************************************************
+     */
+
+    /**
+     * @since 2.9
+     */
+    public interface Gettable {
+        public ValueInstantiator getValueInstantiator();
+    }
+
+    /*
+    /**********************************************************
     /* Standard Base implementation (since 2.8)
     /**********************************************************
      */
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/ValueInstantiators.java b/src/main/java/com/fasterxml/jackson/databind/deser/ValueInstantiators.java
index 59e5eac..3f04f60 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/ValueInstantiators.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/ValueInstantiators.java
@@ -26,7 +26,7 @@
      *   a custom instantiator already)
      *   
      * @return Instantiator to use; either <code>defaultInstantiator</code> that was passed,
-     *   or a custom variant; can not be null.
+     *   or a custom variant; cannot be null.
      */
     public ValueInstantiator findValueInstantiator(DeserializationConfig config,
             BeanDescription beanDesc, ValueInstantiator defaultInstantiator);
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanAsArrayBuilderDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanAsArrayBuilderDeserializer.java
index dabe451..0dbc50d 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanAsArrayBuilderDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanAsArrayBuilderDeserializer.java
@@ -15,7 +15,7 @@
     private static final long serialVersionUID = 1L;
 
     /**
-     * Deserializer we delegate operations that we can not handle.
+     * Deserializer we delegate operations that we cannot handle.
      */
     final protected BeanDeserializerBase _delegate;
 
@@ -25,7 +25,15 @@
     final protected SettableBeanProperty[] _orderedProperties;
 
     final protected AnnotatedMethod _buildMethod;
-        
+
+    /**
+     * Type that the builder will produce, target type; as opposed to
+     * `handledType()` which refers to Builder class.
+     *
+     * @since 2.9
+     */
+    protected final JavaType _targetType;
+
     /*
     /**********************************************************
     /* Life-cycle, construction, initialization
@@ -36,13 +44,17 @@
      * Main constructor used both for creating new instances (by
      * {@link BeanDeserializer#asArrayDeserializer}) and for
      * creating copies with different delegate.
+     *
+     * @since 2.9
      */
     public BeanAsArrayBuilderDeserializer(BeanDeserializerBase delegate,
+            JavaType targetType,
             SettableBeanProperty[] ordered,
             AnnotatedMethod buildMethod)
     {
         super(delegate);
         _delegate = delegate;
+        _targetType = targetType;
         _orderedProperties = ordered;
         _buildMethod = buildMethod;
     }
@@ -60,19 +72,19 @@
     @Override
     public BeanDeserializerBase withObjectIdReader(ObjectIdReader oir) {
         return new BeanAsArrayBuilderDeserializer(_delegate.withObjectIdReader(oir),
-                _orderedProperties, _buildMethod);
+                _targetType, _orderedProperties, _buildMethod);
     }
 
     @Override
     public BeanDeserializerBase withIgnorableProperties(Set<String> ignorableProps) {
         return new BeanAsArrayBuilderDeserializer(_delegate.withIgnorableProperties(ignorableProps),
-                _orderedProperties, _buildMethod);
+                _targetType, _orderedProperties, _buildMethod);
     }
 
     @Override
     public BeanDeserializerBase withBeanProperties(BeanPropertyMap props) {
         return new BeanAsArrayBuilderDeserializer(_delegate.withBeanProperties(props),
-                _orderedProperties, _buildMethod);
+                _targetType, _orderedProperties, _buildMethod);
     }
 
     @Override
@@ -82,6 +94,18 @@
 
     /*
     /**********************************************************
+    /* Overrides
+    /**********************************************************
+     */
+    
+    @Override // since 2.9
+    public Boolean supportsUpdate(DeserializationConfig config) {
+        // 26-Oct-2016, tatu: No, we can't merge Builder-based POJOs as of now
+        return Boolean.FALSE;
+    }
+
+    /*
+    /**********************************************************
     /* JsonDeserializer implementation
     /**********************************************************
      */
@@ -90,7 +114,7 @@
         throws IOException
     {
         try {
-            return _buildMethod.getMember().invoke(builder);
+            return _buildMethod.getMember().invoke(builder, (Object[]) null);
         } catch (Exception e) {
             return wrapInstantiationProblem(e, ctxt);
         }
@@ -130,9 +154,11 @@
             }
             ++i;
         }
-        // Ok; extra fields? Let's fail, unless ignoring extra props is fine
-        if (!_ignoreAllUnknown) {
-            ctxt.reportMappingException("Unexpected JSON values; expected at most %d properties (in JSON Array)",
+        // 09-Nov-2016, tatu: Should call `handleUnknownProperty()` in Context, but it'd give
+        //   non-optimal exception message so...
+        if (!_ignoreAllUnknown && ctxt.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)) {
+            ctxt.reportInputMismatch(handledType(),
+                    "Unexpected JSON values; expected at most %d properties (in JSON Array)",
                     propCount);
             // fall through
         }
@@ -144,50 +170,11 @@
     }
 
     @Override
-    public Object deserialize(JsonParser p, DeserializationContext ctxt, Object builder)
+    public Object deserialize(JsonParser p, DeserializationContext ctxt, Object value)
         throws IOException
     {
-        /* No good way to verify that we have an array... although could I guess
-         * check via JsonParser. So let's assume everything is working fine, for now.
-         */
-        if (_injectables != null) {
-            injectValues(ctxt, builder);
-        }
-        final SettableBeanProperty[] props = _orderedProperties;
-        int i = 0;
-        final int propCount = props.length;
-        while (true) {
-            if (p.nextToken() == JsonToken.END_ARRAY) {
-                return finishBuild(ctxt, builder);
-            }
-            if (i == propCount) {
-                break;
-            }
-            SettableBeanProperty prop = props[i];
-            if (prop != null) { // normal case
-                try {
-                    builder = prop.deserializeSetAndReturn(p, ctxt, builder);
-                } catch (Exception e) {
-                    wrapAndThrow(e, builder, prop.getName(), ctxt);
-                }
-            } else { // just skip?
-                p.skipChildren();
-            }
-            ++i;
-        }
-        
-        // Ok; extra fields? Let's fail, unless ignoring extra props is fine
-        if (!_ignoreAllUnknown) {
-            ctxt.reportWrongTokenException(p, JsonToken.END_ARRAY,
-                    "Unexpected JSON values; expected at most %d properties (in JSON Array)",
-                    propCount);
-            // never gets here
-        }
-        // otherwise, skip until end
-        do {
-            p.skipChildren();
-        } while (p.nextToken() != JsonToken.END_ARRAY);
-        return finishBuild(ctxt, builder);
+        // 26-Oct-2016, tatu: Will fail, but let the original deserializer provide message
+        return _delegate.deserialize(p, ctxt, value);
     }
 
     // needed since 2.1
@@ -247,8 +234,8 @@
             p.skipChildren();
         }
         // Ok; extra fields? Let's fail, unless ignoring extra props is fine
-        if (!_ignoreAllUnknown) {
-            ctxt.reportWrongTokenException(p, JsonToken.END_ARRAY,
+        if (!_ignoreAllUnknown && ctxt.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)) {
+            ctxt.reportWrongTokenException(this, JsonToken.END_ARRAY,
                     "Unexpected JSON value(s); expected at most %d properties (in JSON Array)",
                     propCount);
             // will never reach here as exception has been thrown
@@ -278,6 +265,7 @@
 
         final SettableBeanProperty[] props = _orderedProperties;
         final int propCount = props.length;
+        final Class<?> activeView = _needViewProcesing ? ctxt.getActiveView() : null;
         int i = 0;
         Object builder = null;
         
@@ -287,6 +275,10 @@
                 p.skipChildren();
                 continue;
             }
+            if ((activeView != null) && !prop.visibleInView(activeView)) {
+                p.skipChildren();
+                continue;
+            }
             // if we have already constructed POJO, things are simple:
             if (builder != null) {
                 try {
@@ -314,10 +306,9 @@
                          *   supported (since ordering of elements may not be guaranteed);
                          *   but make explicitly non-supported for now.
                          */
-                        ctxt.reportMappingException("Can not support implicit polymorphic deserialization for POJOs-as-Arrays style: "
-                                +"nominal type %s, actual type %s",
-                                _beanType.getRawClass().getName(), builder.getClass().getName());
-                        return null;
+                        return ctxt.reportBadDefinition(_beanType, String.format(
+"Cannot support implicit polymorphic deserialization for POJOs-as-Arrays style: nominal type %s, actual type %s",
+                                _beanType.getRawClass().getName(), builder.getClass().getName()));
                     }
                 }
                 continue;
@@ -352,7 +343,7 @@
     {
         // Let's start with failure
         return ctxt.handleUnexpectedToken(handledType(), p.getCurrentToken(), p,
-                "Can not deserialize a POJO (of type %s) from non-Array representation (token: %s): "
+                "Cannot deserialize a POJO (of type %s) from non-Array representation (token: %s): "
                 +"type/property designed to be serialized as JSON Array",
                 _beanType.getRawClass().getName(),
                 p.getCurrentToken());
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanAsArrayDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanAsArrayDeserializer.java
index 1cff5ca..2b39004 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanAsArrayDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanAsArrayDeserializer.java
@@ -21,7 +21,7 @@
     private static final long serialVersionUID = 1L;
 
     /**
-     * Deserializer we delegate operations that we can not handle.
+     * Deserializer we delegate operations that we cannot handle.
      */
     protected final BeanDeserializerBase _delegate;
 
@@ -126,8 +126,8 @@
             ++i;
         }
         // Ok; extra fields? Let's fail, unless ignoring extra props is fine
-        if (!_ignoreAllUnknown) {
-            ctxt.reportWrongTokenException(p, JsonToken.END_ARRAY,
+        if (!_ignoreAllUnknown && ctxt.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)) {
+            ctxt.reportWrongTokenException(this, JsonToken.END_ARRAY,
                     "Unexpected JSON values; expected at most %d properties (in JSON Array)",
                     propCount);
             // never gets here
@@ -145,6 +145,11 @@
     {
         // [databind#631]: Assign current value, to be accessible by custom serializers
         p.setCurrentValue(bean);
+
+        if (!p.isExpectedStartArrayToken()) {
+            return _deserializeFromNonArray(p, ctxt);
+        }
+        
         /* No good way to verify that we have an array... although could I guess
          * check via JsonParser. So let's assume everything is working fine, for now.
          */
@@ -175,8 +180,8 @@
         }
         
         // Ok; extra fields? Let's fail, unless ignoring extra props is fine
-        if (!_ignoreAllUnknown) {
-            ctxt.reportWrongTokenException(p, JsonToken.END_ARRAY,
+        if (!_ignoreAllUnknown && ctxt.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)) {
+            ctxt.reportWrongTokenException(this, JsonToken.END_ARRAY,
                     "Unexpected JSON values; expected at most %d properties (in JSON Array)",
                     propCount);
             // never gets here
@@ -222,6 +227,7 @@
         final SettableBeanProperty[] props = _orderedProperties;
         int i = 0;
         final int propCount = props.length;
+
         while (true) {
             if (p.nextToken() == JsonToken.END_ARRAY) {
                 return bean;
@@ -246,7 +252,7 @@
         }
         // Ok; extra fields? Let's fail, unless ignoring extra props is fine
         if (!_ignoreAllUnknown) {
-            ctxt.reportWrongTokenException(p, JsonToken.END_ARRAY,
+            ctxt.reportWrongTokenException(this, JsonToken.END_ARRAY,
                     "Unexpected JSON values; expected at most %d properties (in JSON Array)",
                     propCount);
             // will never reach here as exception has been thrown
@@ -277,13 +283,19 @@
         final int propCount = props.length;
         int i = 0;
         Object bean = null;
-        
+        final Class<?> activeView = _needViewProcesing ? ctxt.getActiveView() : null;
+
         for (; p.nextToken() != JsonToken.END_ARRAY; ++i) {
             SettableBeanProperty prop = (i < propCount) ? props[i] : null;
             if (prop == null) { // we get null if there are extra elements; maybe otherwise too?
                 p.skipChildren();
                 continue;
             }
+            if ((activeView != null) && !prop.visibleInView(activeView)) {
+                p.skipChildren();
+                continue;
+            }
+
             // if we have already constructed POJO, things are simple:
             if (bean != null) {
                 try {
@@ -314,9 +326,10 @@
                          *   supported (since ordering of elements may not be guaranteed);
                          *   but make explicitly non-supported for now.
                          */
-                        ctxt.reportMappingException("Can not support implicit polymorphic deserialization for POJOs-as-Arrays style: "
+                        ctxt.reportBadDefinition(_beanType, String.format(
+                                "Cannot support implicit polymorphic deserialization for POJOs-as-Arrays style: "
                                 +"nominal type %s, actual type %s",
-                                _beanType.getRawClass().getName(), bean.getClass().getName());
+                                _beanType.getRawClass().getName(), bean.getClass().getName()));
                     }
                 }
                 continue;
@@ -350,7 +363,7 @@
         throws IOException
     {
         return ctxt.handleUnexpectedToken(handledType(), p.getCurrentToken(), p,
-                "Can not deserialize a POJO (of type %s) from non-Array representation (token: %s): "
+                "Cannot deserialize a POJO (of type %s) from non-Array representation (token: %s): "
                 +"type/property designed to be serialized as JSON Array",
                 _beanType.getRawClass().getName(),
                 p.getCurrentToken());
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanPropertyMap.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanPropertyMap.java
index 8031ee0..b015bb5 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanPropertyMap.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanPropertyMap.java
@@ -10,7 +10,9 @@
 import com.fasterxml.jackson.databind.DeserializationFeature;
 import com.fasterxml.jackson.databind.JsonDeserializer;
 import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.PropertyName;
 import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
+import com.fasterxml.jackson.databind.util.ClassUtil;
 import com.fasterxml.jackson.databind.util.NameTransformer;
 
 /**
@@ -41,7 +43,7 @@
      * Number of entries stored in the hash area.
      */
     private int _size;
-    
+
     private int _spillCount;
 
     /**
@@ -53,24 +55,120 @@
      * Array of properties in the exact order they were handed in. This is
      * used by as-array serialization, deserialization.
      */
-    private SettableBeanProperty[] _propsInOrder;
+    private final SettableBeanProperty[] _propsInOrder;
 
-    public BeanPropertyMap(boolean caseInsensitive, Collection<SettableBeanProperty> props)
+    /**
+     * Configuration of alias mappings, indexed by unmodified property name
+     * to unmodified aliases, if any; entries only included for properties
+     * that do have aliases.
+     * This is is used for constructing actual reverse lookup mapping, if
+     * needed, taking into account possible case-insensitivity, as well
+     * as possibility of name prefixes.
+     *
+     * @since 2.9
+     */
+    private final Map<String,List<PropertyName>> _aliasDefs;
+
+    /**
+     * Mapping from secondary names (aliases) to primary names.
+     *
+     * @since 2.9
+     */
+    private final Map<String,String> _aliasMapping;
+
+    /**
+     * @since 2.9
+     */
+    public BeanPropertyMap(boolean caseInsensitive, Collection<SettableBeanProperty> props,
+            Map<String,List<PropertyName>> aliasDefs)
     {
         _caseInsensitive = caseInsensitive;
         _propsInOrder = props.toArray(new SettableBeanProperty[props.size()]);
+        _aliasDefs = aliasDefs;
+        _aliasMapping = _buildAliasMapping(aliasDefs);
         init(props);
     }
 
+    /* Copy constructors used when a property can replace existing one
+     *
+     * @since 2.9.6
+     */
+    private BeanPropertyMap(BeanPropertyMap src,
+            SettableBeanProperty newProp, int hashIndex, int orderedIndex)
+    {
+        // First, copy most fields as is:
+        _caseInsensitive = src._caseInsensitive;
+        _hashMask = src._hashMask;
+        _size = src._size;
+        _spillCount = src._spillCount;
+        _aliasDefs = src._aliasDefs;
+        _aliasMapping = src._aliasMapping;
+
+        // but then make deep copy of arrays to modify
+        _hashArea = Arrays.copyOf(src._hashArea, src._hashArea.length);
+        _propsInOrder = Arrays.copyOf(src._propsInOrder, src._propsInOrder.length);
+        _hashArea[hashIndex] = newProp;
+        _propsInOrder[orderedIndex] = newProp;
+    }
+
+    /* Copy constructors used when a property needs to be appended (can't replace)
+     *
+     * @since 2.9.6
+     */
+    private BeanPropertyMap(BeanPropertyMap src,
+            SettableBeanProperty newProp, String key, int slot)
+    {
+        // First, copy most fields as is:
+        _caseInsensitive = src._caseInsensitive;
+        _hashMask = src._hashMask;
+        _size = src._size;
+        _spillCount = src._spillCount;
+        _aliasDefs = src._aliasDefs;
+        _aliasMapping = src._aliasMapping;
+
+        // but then make deep copy of arrays to modify
+        _hashArea = Arrays.copyOf(src._hashArea, src._hashArea.length);
+        int last = src._propsInOrder.length;
+        // and append property at the end of ordering
+        _propsInOrder = Arrays.copyOf(src._propsInOrder, last+1);
+        _propsInOrder[last] = newProp;
+
+        final int hashSize = _hashMask+1;
+        int ix = (slot<<1);
+
+        // primary slot not free?
+        if (_hashArea[ix] != null) {
+            // secondary?
+            ix = (hashSize + (slot >> 1)) << 1;
+            if (_hashArea[ix] != null) {
+                // ok, spill over.
+                ix = ((hashSize + (hashSize >> 1) ) << 1) + _spillCount;
+                _spillCount += 2;
+                if (ix >= _hashArea.length) {
+                    _hashArea = Arrays.copyOf(_hashArea, _hashArea.length + 4);
+                }
+            }
+        }
+        _hashArea[ix] = key;
+        _hashArea[ix+1] = newProp;
+    }
+
+    @Deprecated // since 2.8
+    public BeanPropertyMap(boolean caseInsensitive, Collection<SettableBeanProperty> props)
+    {
+        this(caseInsensitive, props, Collections.<String,List<PropertyName>>emptyMap());
+    }
+
     /**
      * @since 2.8
      */
     protected BeanPropertyMap(BeanPropertyMap base, boolean caseInsensitive)
     {
         _caseInsensitive = caseInsensitive;
+        _aliasDefs = base._aliasDefs;
+        _aliasMapping = base._aliasMapping;
 
-        // 16-May-2016, tatu: Alas, not enough to just change flag, need to re-init
-        //    as well.
+        // 16-May-2016, tatu: Alas, not enough to just change flag, need to re-init as well.
         _propsInOrder = Arrays.copyOf(base._propsInOrder, base._propsInOrder.length);
         init(Arrays.asList(_propsInOrder));
     }
@@ -107,7 +205,7 @@
             if (prop == null) {
                 continue;
             }
-            
+
             String key = getPropertyName(prop);
             int slot = _hashCode(key);
             int ix = (slot<<1);
@@ -125,19 +223,15 @@
                     }
                 }
             }
-//System.err.println(" add '"+key+" at #"+(ix>>1)+"/"+size+" (hashed at "+slot+")");             
             hashed[ix] = key;
             hashed[ix+1] = prop;
+
+            // and aliases
         }
-/*
-for (int i = 0; i < hashed.length; i += 2) {
-System.err.printf("#%02d: %s\n", i>>1, (hashed[i] == null) ? "-" : hashed[i]);
-}
-*/
         _hashArea = hashed;
         _spillCount = spillCount;
     }
-    
+
     private final static int findSize(int size)
     {
         if (size <= 5) {
@@ -157,10 +251,17 @@
     /**
      * @since 2.6
      */
-    public static BeanPropertyMap construct(Collection<SettableBeanProperty> props, boolean caseInsensitive) {
-        return new BeanPropertyMap(caseInsensitive, props);
+    public static BeanPropertyMap construct(Collection<SettableBeanProperty> props,
+            boolean caseInsensitive, Map<String,List<PropertyName>> aliasMapping) {
+        return new BeanPropertyMap(caseInsensitive, props, aliasMapping);
     }
-    
+
+    @Deprecated // since 2.9
+    public static BeanPropertyMap construct(Collection<SettableBeanProperty> props, boolean caseInsensitive) {
+        return construct(props, caseInsensitive,
+                Collections.<String,List<PropertyName>>emptyMap());
+    }
+
     /**
      * Fluent copy method that creates a new instance that is a copy
      * of this instance except for one additional property that is
@@ -176,49 +277,13 @@
         for (int i = 1, end = _hashArea.length; i < end; i += 2) {
             SettableBeanProperty prop = (SettableBeanProperty) _hashArea[i];
             if ((prop != null) && prop.getName().equals(key)) {
-                _hashArea[i] = newProp;
-                _propsInOrder[_findFromOrdered(prop)] = newProp;
-                return this;
+                return new BeanPropertyMap(this, newProp, i, _findFromOrdered(prop));
             }
         }
         // If not, append
         final int slot = _hashCode(key);
-        final int hashSize = _hashMask+1;
-        int ix = (slot<<1);
-        
-        // primary slot not free?
-        if (_hashArea[ix] != null) {
-            // secondary?
-            ix = (hashSize + (slot >> 1)) << 1;
-            if (_hashArea[ix] != null) {
-                // ok, spill over.
-                ix = ((hashSize + (hashSize >> 1) ) << 1) + _spillCount;
-                _spillCount += 2;
-                if (ix >= _hashArea.length) {
-                    _hashArea = Arrays.copyOf(_hashArea, _hashArea.length + 4);
-                    // Uncomment for debugging only
-                    /*
-for (int i = 0; i < _hashArea.length; i += 2) {
-    if (_hashArea[i] != null) {
-        System.err.println("Property #"+(i/2)+" '"+_hashArea[i]+"'...");
-    }
-}
-System.err.println("And new propr #"+slot+" '"+key+"'");
-*/
-                
-                }
-            }
-        }
-        _hashArea[ix] = key;
-        _hashArea[ix+1] = newProp;
 
-        int last = _propsInOrder.length;
-        _propsInOrder = Arrays.copyOf(_propsInOrder, last+1);
-        _propsInOrder[last] = newProp;
-
-        // should we just create a new one? Or is resetting ok?
-        
-        return this;
+        return new BeanPropertyMap(this, newProp, key, slot);
     }
 
     public BeanPropertyMap assignIndexes()
@@ -258,9 +323,16 @@
             newProps.add(_rename(prop, transformer));
         }
         // should we try to re-index? Ordering probably changed but caller probably doesn't want changes...
-        return new BeanPropertyMap(_caseInsensitive, newProps);
+        // 26-Feb-2017, tatu: Probably SHOULD handle renaming wrt Aliases?
+        return new BeanPropertyMap(_caseInsensitive, newProps, _aliasDefs);
     }
 
+    /*
+    /**********************************************************
+    /* Public API, mutators
+    /**********************************************************
+     */
+    
     /**
      * Mutant factory method that will use this instance as the base, and
      * construct an instance that is otherwise same except for excluding
@@ -288,137 +360,47 @@
             }
         }
         // should we try to re-index? Apparently no need
-        return new BeanPropertyMap(_caseInsensitive, newProps);
+        return new BeanPropertyMap(_caseInsensitive, newProps, _aliasDefs);
     }
-    
-    /**
-     * Specialized method that can be used to replace an existing entry
-     * (note: entry MUST exist; otherwise exception is thrown) with
-     * specified replacement.
-     */
+
+    @Deprecated // in 2.9.4 -- must call method that takes old and new property to avoid mismatch
     public void replace(SettableBeanProperty newProp)
     {
         String key = getPropertyName(newProp);
         int ix = _findIndexInHash(key);
-        
-        if (ix >= 0) {
-            SettableBeanProperty prop = (SettableBeanProperty) _hashArea[ix];
-            _hashArea[ix] = newProp;
-            // also, replace in in-order
-            _propsInOrder[_findFromOrdered(prop)] = newProp;
-            return;
+        if (ix < 0) {
+            throw new NoSuchElementException("No entry '"+key+"' found, can't replace");
         }
-        
-        throw new NoSuchElementException("No entry '"+key+"' found, can't replace");
-    }
-
-    private List<SettableBeanProperty> properties() {
-        ArrayList<SettableBeanProperty> p = new ArrayList<SettableBeanProperty>(_size);
-        for (int i = 1, end = _hashArea.length; i < end; i += 2) {
-            SettableBeanProperty prop = (SettableBeanProperty) _hashArea[i];
-            if (prop != null) {
-                p.add(prop);
-            }
-        }
-        return p;
+        SettableBeanProperty prop = (SettableBeanProperty) _hashArea[ix];
+        _hashArea[ix] = newProp;
+        // also, replace in in-order
+        _propsInOrder[_findFromOrdered(prop)] = newProp;
     }
 
     /**
-     * Accessor for traversing over all contained properties.
+     * Specialized method that can be used to replace an existing entry
+     * (note: entry MUST exist; otherwise exception is thrown) with
+     * specified replacement.
+     *
+     * @since 2.9.4
      */
-    @Override
-    public Iterator<SettableBeanProperty> iterator() {
-        return properties().iterator();
-    }
-
-    /**
-     * Method that will re-create initial insertion-ordering of
-     * properties contained in this map. Note that if properties
-     * have been removed, array may contain nulls; otherwise
-     * it should be consecutive.
-     * 
-     * @since 2.1
-     */
-    public SettableBeanProperty[] getPropertiesInInsertionOrder() {
-        return _propsInOrder;
-    }
-
-    // Confining this case insensitivity to this function (and the find method) in case we want to
-    // apply a particular locale to the lower case function.  For now, using the default.
-    protected final String getPropertyName(SettableBeanProperty prop) {
-        return _caseInsensitive ? prop.getName().toLowerCase() : prop.getName();
-    }
-
-    /**
-     * @since 2.3
-     */
-    public SettableBeanProperty find(int index)
+    public void replace(SettableBeanProperty origProp, SettableBeanProperty newProp)
     {
-        // note: will scan the whole area, including primary, secondary and
-        // possible spill-area
-        for (int i = 1, end = _hashArea.length; i < end; i += 2) {
-            SettableBeanProperty prop = (SettableBeanProperty) _hashArea[i];
-            if ((prop != null) && (index == prop.getPropertyIndex())) {
-                return prop;
+        int i = 1;
+        int end = _hashArea.length;
+
+        for (;; i += 2) {
+            if (i > end) {
+                throw new NoSuchElementException("No entry '"+origProp.getName()+"' found, can't replace");
+            }
+            if (_hashArea[i] == origProp) {
+                _hashArea[i] = newProp;
+                break;
             }
         }
-        return null;
+        _propsInOrder[_findFromOrdered(origProp)] = newProp;
     }
 
-    public SettableBeanProperty find(String key)
-    {
-        if (key == null) {
-            throw new IllegalArgumentException("Can not pass null property name");
-        }
-        if (_caseInsensitive) {
-            key = key.toLowerCase();
-        }
-            
-        // inlined `_hashCode(key)`
-        int slot = key.hashCode() & _hashMask;
-//        int h = key.hashCode();
-//        int slot = (h + (h >> 13)) & _hashMask;
-
-        int ix = (slot<<1);
-        Object match = _hashArea[ix];
-        if ((match == key) || key.equals(match)) {
-            return (SettableBeanProperty) _hashArea[ix+1];
-        }
-        return _find2(key, slot, match);
-    }
-
-    private final SettableBeanProperty _find2(String key, int slot, Object match)
-    {
-        if (match == null) {
-            return null;
-        }
-        // no? secondary?
-        int hashSize = _hashMask+1;
-        int ix = hashSize + (slot>>1) << 1;
-        match = _hashArea[ix];
-        if (key.equals(match)) {
-            return (SettableBeanProperty) _hashArea[ix+1];
-        }
-        if (match != null) { // _findFromSpill(...)
-            int i = (hashSize + (hashSize>>1)) << 1;
-            for (int end = i + _spillCount; i < end; i += 2) {
-                match = _hashArea[i];
-                if ((match == key) || key.equals(match)) {
-                    return (SettableBeanProperty) _hashArea[i+1];
-                }
-            }
-        }
-        return null;
-    }
-    
-    /*
-    /**********************************************************
-    /* Public API
-    /**********************************************************
-     */
-
-    public int size() { return _size; }
-
     /**
      * Specialized method for removing specified existing entry.
      * NOTE: entry MUST exist, otherwise an exception is thrown.
@@ -452,6 +434,181 @@
         init(props);
     }
 
+    /*
+    /**********************************************************
+    /* Public API, simple accessors
+    /**********************************************************
+     */
+
+    public int size() { return _size; }
+
+    /**
+     * @since 2.9
+     */
+    public boolean isCaseInsensitive() {
+        return _caseInsensitive;
+    }
+
+    /**
+     * @since 2.9
+     */
+    public boolean hasAliases() {
+        return !_aliasDefs.isEmpty();
+    }
+
+    /**
+     * Accessor for traversing over all contained properties.
+     */
+    @Override
+    public Iterator<SettableBeanProperty> iterator() {
+        return _properties().iterator();
+    }
+
+    private List<SettableBeanProperty> _properties() {
+        ArrayList<SettableBeanProperty> p = new ArrayList<SettableBeanProperty>(_size);
+        for (int i = 1, end = _hashArea.length; i < end; i += 2) {
+            SettableBeanProperty prop = (SettableBeanProperty) _hashArea[i];
+            if (prop != null) {
+                p.add(prop);
+            }
+        }
+        return p;
+    }
+    
+    /**
+     * Method that will re-create initial insertion-ordering of
+     * properties contained in this map. Note that if properties
+     * have been removed, array may contain nulls; otherwise
+     * it should be consecutive.
+     * 
+     * @since 2.1
+     */
+    public SettableBeanProperty[] getPropertiesInInsertionOrder() {
+        return _propsInOrder;
+    }
+
+    // Confining this case insensitivity to this function (and the find method) in case we want to
+    // apply a particular locale to the lower case function.  For now, using the default.
+    protected final String getPropertyName(SettableBeanProperty prop) {
+        return _caseInsensitive ? prop.getName().toLowerCase() : prop.getName();
+    }
+
+    /*
+    /**********************************************************
+    /* Public API, property lookup
+    /**********************************************************
+     */
+    
+    /**
+     * @since 2.3
+     */
+    public SettableBeanProperty find(int index)
+    {
+        // note: will scan the whole area, including primary, secondary and
+        // possible spill-area
+        for (int i = 1, end = _hashArea.length; i < end; i += 2) {
+            SettableBeanProperty prop = (SettableBeanProperty) _hashArea[i];
+            if ((prop != null) && (index == prop.getPropertyIndex())) {
+                return prop;
+            }
+        }
+        return null;
+    }
+
+    public SettableBeanProperty find(String key)
+    {
+        if (key == null) {
+            throw new IllegalArgumentException("Cannot pass null property name");
+        }
+        if (_caseInsensitive) {
+            key = key.toLowerCase();
+        }
+
+        // inlined `_hashCode(key)`
+        int slot = key.hashCode() & _hashMask;
+//        int h = key.hashCode();
+//        int slot = (h + (h >> 13)) & _hashMask;
+
+        int ix = (slot<<1);
+        Object match = _hashArea[ix];
+        if ((match == key) || key.equals(match)) {
+            return (SettableBeanProperty) _hashArea[ix+1];
+        }
+        return _find2(key, slot, match);
+    }
+
+    private final SettableBeanProperty _find2(String key, int slot, Object match)
+    {
+        if (match == null) {
+            // 26-Feb-2017, tatu: Need to consider aliases
+            return _findWithAlias(_aliasMapping.get(key));
+        }
+        // no? secondary?
+        int hashSize = _hashMask+1;
+        int ix = hashSize + (slot>>1) << 1;
+        match = _hashArea[ix];
+        if (key.equals(match)) {
+            return (SettableBeanProperty) _hashArea[ix+1];
+        }
+        if (match != null) { // _findFromSpill(...)
+            int i = (hashSize + (hashSize>>1)) << 1;
+            for (int end = i + _spillCount; i < end; i += 2) {
+                match = _hashArea[i];
+                if ((match == key) || key.equals(match)) {
+                    return (SettableBeanProperty) _hashArea[i+1];
+                }
+            }
+        }
+        // 26-Feb-2017, tatu: Need to consider aliases
+        return _findWithAlias(_aliasMapping.get(key));
+    }
+
+    private SettableBeanProperty _findWithAlias(String keyFromAlias)
+    {
+        if (keyFromAlias == null) {
+            return null;
+        }
+        // NOTE: need to inline much of handling do avoid cyclic calls via alias
+        // first, inlined main `find(String)`
+        int slot = _hashCode(keyFromAlias);
+        int ix = (slot<<1);
+        Object match = _hashArea[ix];
+        if (keyFromAlias.equals(match)) {
+            return (SettableBeanProperty) _hashArea[ix+1];
+        }
+        if (match == null) {
+            return null;
+        }
+        return _find2ViaAlias(keyFromAlias, slot, match);
+    }
+
+    private SettableBeanProperty _find2ViaAlias(String key, int slot, Object match)
+    {
+        // no? secondary?
+        int hashSize = _hashMask+1;
+        int ix = hashSize + (slot>>1) << 1;
+        match = _hashArea[ix];
+        if (key.equals(match)) {
+            return (SettableBeanProperty) _hashArea[ix+1];
+        }
+        if (match != null) { // _findFromSpill(...)
+            int i = (hashSize + (hashSize>>1)) << 1;
+            for (int end = i + _spillCount; i < end; i += 2) {
+                match = _hashArea[i];
+                if ((match == key) || key.equals(match)) {
+                    return (SettableBeanProperty) _hashArea[i+1];
+                }
+            }
+        }
+        return null;
+    }
+
+    /*
+    /**********************************************************
+    /* Public API, deserialization support
+    /**********************************************************
+     */
+
     /**
      * Convenience method that tries to find property with given name, and
      * if it is found, call {@link SettableBeanProperty#deserializeAndSet}
@@ -476,6 +633,12 @@
         return true;
     }
 
+    /*
+    /**********************************************************
+    /* Std method overrides
+    /**********************************************************
+     */
+    
     @Override
     public String toString()
     {
@@ -495,6 +658,11 @@
             sb.append(')');
         }
         sb.append(']');
+        if (!_aliasDefs.isEmpty()) {
+            sb.append("(aliases: ");
+            sb.append(_aliasDefs);
+            sb.append(")");
+        }
         return sb.toString();
     }
     
@@ -531,9 +699,7 @@
             t = t.getCause();
         }
         // Errors to be passed as is
-        if (t instanceof Error) {
-            throw (Error) t;
-        }
+        ClassUtil.throwIfError(t);
         // StackOverflowErrors are tricky ones; need to be careful...
         boolean wrap = (ctxt == null) || ctxt.isEnabled(DeserializationFeature.WRAP_EXCEPTIONS);
         // Ditto for IOExceptions; except we may want to wrap JSON exceptions
@@ -542,9 +708,7 @@
                 throw (IOException) t;
             }
         } else if (!wrap) { // allow disabling wrapping for unchecked exceptions
-            if (t instanceof RuntimeException) {
-                throw (RuntimeException) t;
-            }
+            ClassUtil.throwIfRTE(t);
         }
         throw JsonMappingException.wrapWithPath(t, bean, fieldName);
     }
@@ -604,4 +768,27 @@
         */
         return key.hashCode() & _hashMask;
     }
+
+    // @since 2.9
+    private Map<String,String> _buildAliasMapping(Map<String,List<PropertyName>> defs)
+    {
+        if ((defs == null) || defs.isEmpty()) {
+            return Collections.emptyMap();
+        }
+        Map<String,String> aliases = new HashMap<>();
+        for (Map.Entry<String,List<PropertyName>> entry : defs.entrySet()) {
+            String key = entry.getKey();
+            if (_caseInsensitive) {
+                key = key.toLowerCase();
+            }
+            for (PropertyName pn : entry.getValue()) {
+                String mapped = pn.getSimpleName();
+                if (_caseInsensitive) {
+                    mapped = mapped.toLowerCase();
+                }
+                aliases.put(mapped, key);
+            }
+        }
+        return aliases;
+    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/CreatorCandidate.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/CreatorCandidate.java
new file mode 100644
index 0000000..64ab178
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/CreatorCandidate.java
@@ -0,0 +1,122 @@
+package com.fasterxml.jackson.databind.deser.impl;
+
+import com.fasterxml.jackson.annotation.JacksonInject;
+import com.fasterxml.jackson.databind.AnnotationIntrospector;
+import com.fasterxml.jackson.databind.PropertyName;
+import com.fasterxml.jackson.databind.introspect.AnnotatedParameter;
+import com.fasterxml.jackson.databind.introspect.AnnotatedWithParams;
+import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
+
+public final class CreatorCandidate
+{
+    protected final AnnotationIntrospector _intr;
+    protected final AnnotatedWithParams _creator;
+    protected final int _paramCount;
+    protected final Param[] _params;
+
+    protected CreatorCandidate(AnnotationIntrospector intr,
+            AnnotatedWithParams ct, Param[] params, int count) {
+        _intr = intr;
+        _creator = ct;
+        _params = params;
+        _paramCount = count;
+    }
+
+    public static CreatorCandidate construct(AnnotationIntrospector intr,
+            AnnotatedWithParams creator, BeanPropertyDefinition[] propDefs)
+    {
+        final int pcount = creator.getParameterCount();
+        Param[] params = new Param[pcount];
+        for (int i = 0; i < pcount; ++i) {
+            AnnotatedParameter annParam = creator.getParameter(i);
+            JacksonInject.Value injectId = intr.findInjectableValue(annParam);
+            params[i] = new Param(annParam, (propDefs == null) ? null : propDefs[i], injectId);
+        }
+        return new CreatorCandidate(intr, creator, params, pcount);
+    }
+
+    public AnnotatedWithParams creator() { return _creator; }
+    public int paramCount() { return _paramCount; }
+    public JacksonInject.Value injection(int i) { return _params[i].injection; }
+    public AnnotatedParameter parameter(int i) { return _params[i].annotated; }
+    public BeanPropertyDefinition propertyDef(int i) { return _params[i].propDef; }
+
+    public PropertyName paramName(int i) {
+        BeanPropertyDefinition propDef = _params[i].propDef;
+        if (propDef != null) {
+            return propDef.getFullName();
+        }
+        return null;
+    }
+
+    public PropertyName explicitParamName(int i) {
+        BeanPropertyDefinition propDef = _params[i].propDef;
+        if (propDef != null) {
+            if (propDef.isExplicitlyNamed()) {
+                return propDef.getFullName();
+            }
+        }
+        return null;
+    }
+    
+    public PropertyName findImplicitParamName(int i) {
+        String str = _intr.findImplicitPropertyName(_params[i].annotated);
+        if (str != null && !str.isEmpty()) {
+            return PropertyName.construct(str);
+        }
+        return null;
+    }
+
+    /**
+     * Specialized accessor that finds index of the one and only parameter
+     * with NO injection and returns that; or, if none or more than one found,
+     * returns -1.
+     */
+    public int findOnlyParamWithoutInjection()
+    {
+        int missing = -1;
+        for (int i = 0; i < _paramCount; ++i) {
+            if (_params[i].injection == null) {
+                if (missing >= 0) {
+                    return -1;
+                }
+                missing = i;
+            }
+        }
+        return missing;
+    }
+
+    @Override
+    public String toString() {
+        return _creator.toString();
+    }
+
+    public final static class Param {
+        public final AnnotatedParameter annotated;
+        public final BeanPropertyDefinition propDef;
+        public final JacksonInject.Value injection;
+
+        public Param(AnnotatedParameter p, BeanPropertyDefinition pd,
+                JacksonInject.Value i)
+        {
+            annotated = p;
+            propDef = pd;
+            injection = i;
+        }
+
+        public PropertyName fullName() {
+            if (propDef == null) {
+                return null;
+            }
+            return propDef.getFullName();
+        }
+
+        public boolean hasFullName() {
+            if (propDef == null) {
+                return false;
+            }
+            PropertyName n = propDef.getFullName();
+            return n.hasSimpleName();
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/CreatorCollector.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/CreatorCollector.java
index 2999341..978c408 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/CreatorCollector.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/CreatorCollector.java
@@ -7,7 +7,6 @@
 
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.cfg.MapperConfig;
-import com.fasterxml.jackson.databind.deser.CreatorProperty;
 import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
 import com.fasterxml.jackson.databind.deser.ValueInstantiator;
 import com.fasterxml.jackson.databind.deser.std.StdValueInstantiator;
@@ -69,11 +68,10 @@
 
     protected SettableBeanProperty[] _propertyBasedArgs;
 
-    protected AnnotatedParameter _incompleteParameter;
-
     /*
-     * /********************************************************** /* Life-cycle
-     * /**********************************************************
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
      */
 
     public CreatorCollector(BeanDescription beanDesc, MapperConfig<?> config) {
@@ -83,11 +81,13 @@
                 .isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS);
     }
 
-    public ValueInstantiator constructValueInstantiator(
-            DeserializationConfig config) {
-        final JavaType delegateType = _computeDelegateType(
+    public ValueInstantiator constructValueInstantiator(DeserializationContext ctxt)
+        throws JsonMappingException
+    {
+        final DeserializationConfig config = ctxt.getConfig();
+        final JavaType delegateType = _computeDelegateType(ctxt,
                 _creators[C_DELEGATE], _delegateArgs);
-        final JavaType arrayDelegateType = _computeDelegateType(
+        final JavaType arrayDelegateType = _computeDelegateType(ctxt,
                 _creators[C_ARRAY_DELEGATE], _arrayDelegateArgs);
         final JavaType type = _beanDesc.getType();
 
@@ -108,13 +108,13 @@
         inst.configureFromLongCreator(_creators[C_LONG]);
         inst.configureFromDoubleCreator(_creators[C_DOUBLE]);
         inst.configureFromBooleanCreator(_creators[C_BOOLEAN]);
-        inst.configureIncompleteParameter(_incompleteParameter);
         return inst;
     }
 
     /*
-     * /********************************************************** /* Setters
-     * /**********************************************************
+    /**********************************************************
+    /* Setters
+    /**********************************************************
      */
 
     /**
@@ -131,8 +131,7 @@
         _creators[C_DEFAULT] = _fixAccess(creator);
     }
 
-    public void addStringCreator(AnnotatedWithParams creator,
-            boolean explicit) {
+    public void addStringCreator(AnnotatedWithParams creator, boolean explicit) {
         verifyNonDup(creator, C_STRING, explicit);
     }
 
@@ -144,19 +143,19 @@
         verifyNonDup(creator, C_LONG, explicit);
     }
 
-    public void addDoubleCreator(AnnotatedWithParams creator,
-            boolean explicit) {
+    public void addDoubleCreator(AnnotatedWithParams creator, boolean explicit) {
         verifyNonDup(creator, C_DOUBLE, explicit);
     }
 
-    public void addBooleanCreator(AnnotatedWithParams creator,
-            boolean explicit) {
+    public void addBooleanCreator(AnnotatedWithParams creator, boolean explicit) {
         verifyNonDup(creator, C_BOOLEAN, explicit);
     }
 
     public void addDelegatingCreator(AnnotatedWithParams creator,
-            boolean explicit, SettableBeanProperty[] injectables) {
-        if (creator.getParameterType(0).isCollectionLikeType()) {
+            boolean explicit, SettableBeanProperty[] injectables,
+            int delegateeIndex)
+    {
+        if (creator.getParameterType(delegateeIndex).isCollectionLikeType()) {
             if (verifyNonDup(creator, C_ARRAY_DELEGATE, explicit)) {
                 _arrayDelegateArgs = injectables;
             }
@@ -168,7 +167,8 @@
     }
 
     public void addPropertyCreator(AnnotatedWithParams creator,
-            boolean explicit, SettableBeanProperty[] properties) {
+            boolean explicit, SettableBeanProperty[] properties)
+    {
         if (verifyNonDup(creator, C_PROPS, explicit)) {
             // Better ensure we have no duplicate names either...
             if (properties.length > 1) {
@@ -177,15 +177,14 @@
                     String name = properties[i].getName();
                     // Need to consider Injectables, which may not have
                     // a name at all, and need to be skipped
-                    if (name.length() == 0
-                            && properties[i].getInjectableValueId() != null) {
+                    if (name.isEmpty() && (properties[i].getInjectableValueId() != null)) {
                         continue;
                     }
                     Integer old = names.put(name, Integer.valueOf(i));
                     if (old != null) {
                         throw new IllegalArgumentException(String.format(
-                                "Duplicate creator property \"%s\" (index %s vs %d)",
-                                name, old, i));
+                                "Duplicate creator property \"%s\" (index %s vs %d) for type %s ",
+                                name, old, i, ClassUtil.nameOf(_beanDesc.getBeanClass())));
                     }
                 }
             }
@@ -193,54 +192,10 @@
         }
     }
 
-    public void addIncompeteParameter(AnnotatedParameter parameter) {
-        if (_incompleteParameter == null) {
-            _incompleteParameter = parameter;
-        }
-    }
-
-    // Bunch of methods deprecated in 2.5, to be removed from 2.6 or later
-
-    @Deprecated // since 2.5
-    public void addStringCreator(AnnotatedWithParams creator) {
-        addStringCreator(creator, false);
-    }
-
-    @Deprecated // since 2.5
-    public void addIntCreator(AnnotatedWithParams creator) {
-        addBooleanCreator(creator, false);
-    }
-
-    @Deprecated // since 2.5
-    public void addLongCreator(AnnotatedWithParams creator) {
-        addBooleanCreator(creator, false);
-    }
-
-    @Deprecated // since 2.5
-    public void addDoubleCreator(AnnotatedWithParams creator) {
-        addBooleanCreator(creator, false);
-    }
-
-    @Deprecated // since 2.5
-    public void addBooleanCreator(AnnotatedWithParams creator) {
-        addBooleanCreator(creator, false);
-    }
-
-    @Deprecated // since 2.5
-    public void addDelegatingCreator(AnnotatedWithParams creator,
-            CreatorProperty[] injectables) {
-        addDelegatingCreator(creator, false, injectables);
-    }
-
-    @Deprecated // since 2.5
-    public void addPropertyCreator(AnnotatedWithParams creator,
-            CreatorProperty[] properties) {
-        addPropertyCreator(creator, false, properties);
-    }
-
     /*
-     * /********************************************************** /* Accessors
-     * /**********************************************************
+    /**********************************************************
+    /* Accessors
+    /**********************************************************
      */
 
     /**
@@ -265,12 +220,15 @@
     }
 
     /*
-     * /********************************************************** /* Helper
-     * methods /**********************************************************
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
      */
 
-    private JavaType _computeDelegateType(AnnotatedWithParams creator,
-            SettableBeanProperty[] delegateArgs) {
+    private JavaType _computeDelegateType(DeserializationContext ctxt,
+            AnnotatedWithParams creator, SettableBeanProperty[] delegateArgs)
+        throws JsonMappingException
+    {
         if (!_hasNonDefaultCreator || (creator == null)) {
             return null;
         }
@@ -284,7 +242,28 @@
                 }
             }
         }
-        return creator.getParameterType(ix);
+        final DeserializationConfig config = ctxt.getConfig();
+
+        // 03-May-2018, tatu: need to check possible annotation-based
+        //   custom deserializer [databind#2012],
+        //   type refinement(s) [databind#2016]. 
+        JavaType baseType = creator.getParameterType(ix);
+        AnnotationIntrospector intr = config.getAnnotationIntrospector();
+        if (intr != null) {
+            AnnotatedParameter delegate = creator.getParameter(ix);
+            
+            // First: custom deserializer(s):
+            Object deserDef = intr.findDeserializer(delegate);
+            if (deserDef != null) {
+                JsonDeserializer<Object> deser = ctxt.deserializerInstance(delegate, deserDef);
+                baseType = baseType.withValueHandler(deser);
+            } else {
+                // Second: type refinement(s), if no explicit deserializer was located
+                baseType = intr.refineDeserializationType(config,
+                        delegate, baseType);
+            }
+        }
+        return baseType;
     }
 
     private <T extends AnnotatedMember> T _fixAccess(T member) {
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/ExternalTypeHandler.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ExternalTypeHandler.java
index 4069f38..9ff1c0b 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/ExternalTypeHandler.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ExternalTypeHandler.java
@@ -18,16 +18,27 @@
  */
 public class ExternalTypeHandler
 {
+    private final JavaType _beanType;
+
     private final ExtTypedProperty[] _properties;
-    private final HashMap<String, Integer> _nameToPropertyIndex;
+
+    /**
+     * Mapping from external property ids to one or more indexes;
+     * in most cases single index as <code>Integer</code>, but
+     * occasionally same name maps to multiple ones: if so,
+     * <code>List&lt;Integer&gt;</code>.
+     */
+    private final Map<String, Object> _nameToPropertyIndex;
 
     private final String[] _typeIds;
     private final TokenBuffer[] _tokens;
 
-    protected ExternalTypeHandler(ExtTypedProperty[] properties,
-            HashMap<String, Integer> nameToPropertyIndex,
+    protected ExternalTypeHandler(JavaType beanType,
+            ExtTypedProperty[] properties,
+            Map<String, Object> nameToPropertyIndex,
             String[] typeIds, TokenBuffer[] tokens)
     {
+        _beanType = beanType;
         _properties = properties;
         _nameToPropertyIndex = nameToPropertyIndex;
         _typeIds = typeIds;
@@ -36,6 +47,7 @@
 
     protected ExternalTypeHandler(ExternalTypeHandler h)
     {
+        _beanType = h._beanType;
         _properties = h._properties;
         _nameToPropertyIndex = h._nameToPropertyIndex;
         int len = _properties.length;
@@ -44,6 +56,13 @@
     }
 
     /**
+     * @since 2.9
+     */
+    public static Builder builder(JavaType beanType) {
+        return new Builder(beanType);
+    }
+
+    /**
      * Method called to start collection process by creating non-blueprint
      * instances.
      */
@@ -54,23 +73,43 @@
     /**
      * Method called to see if given property/value pair is an external type
      * id; and if so handle it. This is <b>only</b> to be called in case
-     * containing POJO has similarly named property as the external type id;
+     * containing POJO has similarly named property as the external type id AND
+     * value is of scalar type:
      * otherwise {@link #handlePropertyValue} should be called instead.
      */
+    @SuppressWarnings("unchecked")
     public boolean handleTypePropertyValue(JsonParser p, DeserializationContext ctxt,
             String propName, Object bean)
         throws IOException
     {
-        Integer I = _nameToPropertyIndex.get(propName);
-        if (I == null) {
+        Object ob = _nameToPropertyIndex.get(propName);
+        if (ob == null) {
             return false;
         }
-        int index = I.intValue();
+        final String typeId = p.getText();
+        // 28-Nov-2016, tatu: For [databind#291], need separate handling
+        if (ob instanceof List<?>) {
+            boolean result = false;
+            for (Integer index : (List<Integer>) ob) {
+                if (_handleTypePropertyValue(p, ctxt, propName, bean,
+                        typeId, index.intValue())) {
+                    result = true;
+                }
+            }
+            return result;
+        }
+        return _handleTypePropertyValue(p, ctxt, propName, bean,
+                typeId, ((Integer) ob).intValue());
+    }
+
+    private final boolean _handleTypePropertyValue(JsonParser p, DeserializationContext ctxt,
+            String propName, Object bean, String typeId, int index)
+        throws IOException
+    {
         ExtTypedProperty prop = _properties[index];
-        if (!prop.hasTypePropertyName(propName)) {
+        if (!prop.hasTypePropertyName(propName)) { // when could/should this ever happen?
             return false;
         }
-        String typeId = p.getText();
         // note: can NOT skip child values (should always be String anyway)
         boolean canDeserialize = (bean != null) && (_tokens[index] != null);
         // Minor optimization: deserialize properties as soon as we have all we need:
@@ -92,14 +131,44 @@
      * 
      * @return True, if the given property was properly handled
      */
+    @SuppressWarnings("unchecked")
     public boolean handlePropertyValue(JsonParser p, DeserializationContext ctxt,
             String propName, Object bean) throws IOException
     {
-        Integer I = _nameToPropertyIndex.get(propName);
-        if (I == null) {
+        Object ob = _nameToPropertyIndex.get(propName);
+        if (ob == null) {
             return false;
         }
-        int index = I.intValue();
+        // 28-Nov-2016, tatu: For [databind#291], need separate handling
+        if (ob instanceof List<?>) {
+            Iterator<Integer> it = ((List<Integer>) ob).iterator();
+            Integer index = it.next();
+
+            ExtTypedProperty prop = _properties[index];
+            // For now, let's assume it's same type (either type id OR value)
+            // for all mappings, so we'll only check first one
+            if (prop.hasTypePropertyName(propName)) {
+                String typeId = p.getText();
+                p.skipChildren();
+                _typeIds[index] = typeId;
+                while (it.hasNext()) {
+                    _typeIds[it.next()] = typeId;
+                }
+            } else {
+                @SuppressWarnings("resource")
+                TokenBuffer tokens = new TokenBuffer(p, ctxt);
+                tokens.copyCurrentStructure(p);
+                _tokens[index] = tokens;
+                while (it.hasNext()) {
+                    _tokens[it.next()] = tokens;
+                }
+            }
+            return true;
+        }
+
+        // Otherwise only maps to a single value, in which case we can
+        // handle things in bit more optimal way...
+        int index = ((Integer) ob).intValue();
         ExtTypedProperty prop = _properties[index];
         boolean canDeserialize;
         if (prop.hasTypePropertyName(propName)) {
@@ -113,9 +182,8 @@
             _tokens[index] = tokens;
             canDeserialize = (bean != null) && (_typeIds[index] != null);
         }
-        /* Minor optimization: let's deserialize properties as soon as
-         * we have all pertinent information:
-         */
+        // Minor optimization: let's deserialize properties as soon as
+        // we have all pertinent information:
         if (canDeserialize) {
             String typeId = _typeIds[index];
             // clear stored data, to avoid deserializing+setting twice:
@@ -146,7 +214,7 @@
                 // [databind#118]: Need to mind natural types, for which no type id
                 // will be included.
                 JsonToken t = tokens.firstToken();
-                if (t != null && t.isScalarValue()) {
+                if (t.isScalarValue()) { // can't be null as we never store empty buffers
                     JsonParser buffered = tokens.asParser(p);
                     buffered.nextToken();
                     SettableBeanProperty extProp = _properties[i].getProperty();
@@ -157,7 +225,8 @@
                     }
                     // 26-Oct-2012, tatu: As per [databind#94], must allow use of 'defaultImpl'
                     if (!_properties[i].hasDefaultType()) {
-                        ctxt.reportMappingException("Missing external type id property '%s'",
+                        ctxt.reportInputMismatch(bean.getClass(),
+                                "Missing external type id property '%s'",
                                 _properties[i].getTypePropertyName());                                
                     } else  {
                         typeId = _properties[i].getDefaultTypeId();
@@ -168,7 +237,8 @@
 
                 if(prop.isRequired() ||
                         ctxt.isEnabled(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY)) {
-                    ctxt.reportMappingException("Missing property '%s' for external type id '%s'",
+                    ctxt.reportInputMismatch(bean.getClass(),
+                            "Missing property '%s' for external type id '%s'",
                             prop.getName(), _properties[i].getTypePropertyName());
                 }
                 return bean;
@@ -192,7 +262,6 @@
         for (int i = 0; i < len; ++i) {
             String typeId = _typeIds[i];
             final ExtTypedProperty extProp = _properties[i];
-
             if (typeId == null) {
                 // let's allow missing both type and property (may already have been set, too)
                 if (_tokens[i] == null) {
@@ -201,14 +270,16 @@
                 // but not just one
                 // 26-Oct-2012, tatu: As per [databind#94], must allow use of 'defaultImpl'
                 if (!extProp.hasDefaultType()) {
-                    ctxt.reportMappingException("Missing external type id property '%s'",
+                    ctxt.reportInputMismatch(_beanType,
+                            "Missing external type id property '%s'",
                             extProp.getTypePropertyName());
                 } else {
                     typeId = extProp.getDefaultTypeId();
                 }
             } else if (_tokens[i] == null) {
                 SettableBeanProperty prop = extProp.getProperty();
-                ctxt.reportMappingException("Missing property '%s' for external type id '%s'",
+                ctxt.reportInputMismatch(_beanType,
+                        "Missing property '%s' for external type id '%s'",
                         prop.getName(), _properties[i].getTypePropertyName());
             }
             values[i] = _deserialize(p, ctxt, i, typeId);
@@ -222,11 +293,21 @@
                 SettableBeanProperty typeProp = extProp.getTypeProperty();
                 // for now, should only be needed for creator properties, too
                 if ((typeProp != null) && (typeProp.getCreatorIndex() >= 0)) {
-                    buffer.assignParameter(typeProp, typeId);
+                    // 31-May-2018, tatu: [databind#1328] if id is NOT plain `String`, need to
+                    //    apply deserializer... fun fun.
+                    final Object v;
+                    if (typeProp.getType().hasRawClass(String.class)) {
+                        v = typeId;
+                    } else {
+                        TokenBuffer tb = new TokenBuffer(p, ctxt);
+                        tb.writeString(typeId);
+                        v = typeProp.getValueDeserializer().deserialize(tb.asParserOnFirstToken(), ctxt);
+                        tb.close();
+                    }
+                    buffer.assignParameter(typeProp, v);
                 }
             }
         }
-
         Object bean = creator.build(ctxt, buffer);
         // third: assign non-creator properties
         for (int i = 0; i < len; ++i) {
@@ -294,17 +375,39 @@
     
     public static class Builder
     {
-        private final ArrayList<ExtTypedProperty> _properties = new ArrayList<ExtTypedProperty>();
-        private final HashMap<String, Integer> _nameToPropertyIndex = new HashMap<String, Integer>();
+        private final JavaType _beanType;
+
+        private final List<ExtTypedProperty> _properties = new ArrayList<>();
+        private final Map<String, Object> _nameToPropertyIndex = new HashMap<>();
+
+        protected Builder(JavaType t) {
+            _beanType = t;
+        }
 
         public void addExternal(SettableBeanProperty property, TypeDeserializer typeDeser)
         {
             Integer index = _properties.size();
             _properties.add(new ExtTypedProperty(property, typeDeser));
-            _nameToPropertyIndex.put(property.getName(), index);
-            _nameToPropertyIndex.put(typeDeser.getPropertyName(), index);
+            _addPropertyIndex(property.getName(), index);
+            _addPropertyIndex(typeDeser.getPropertyName(), index);
         }
 
+        private void _addPropertyIndex(String name, Integer index) {
+            Object ob = _nameToPropertyIndex.get(name);
+            if (ob == null) {
+                _nameToPropertyIndex.put(name, index);
+            } else if (ob instanceof List<?>) {
+                @SuppressWarnings("unchecked")
+                List<Object> list = (List<Object>) ob;
+                list.add(index);
+            } else {
+                List<Object> list = new LinkedList<>();
+                list.add(ob);
+                list.add(index);
+                _nameToPropertyIndex.put(name, list);
+            }
+        }
+        
         /**
          * Method called after all external properties have been assigned, to further
          * link property with polymorphic value with possible property for type id
@@ -325,13 +428,8 @@
                 }
                 extProps[i] = extProp;
             }
-            return new ExternalTypeHandler(extProps, _nameToPropertyIndex, null, null);
-        }
-
-        @Deprecated // since 2.8; may be removed as early as 2.9
-        public ExternalTypeHandler build() {
-            return new ExternalTypeHandler(_properties.toArray(new ExtTypedProperty[_properties.size()]),
-                    _nameToPropertyIndex, null, null);
+            return new ExternalTypeHandler(_beanType, extProps, _nameToPropertyIndex,
+                    null, null);
         }
     }
 
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/FailingDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/FailingDeserializer.java
index a777bde..9df6742 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/FailingDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/FailingDeserializer.java
@@ -1,5 +1,7 @@
 package com.fasterxml.jackson.databind.deser.impl;
 
+import java.io.IOException;
+
 import com.fasterxml.jackson.core.JsonParser;
 import com.fasterxml.jackson.databind.DeserializationContext;
 import com.fasterxml.jackson.databind.JsonMappingException;
@@ -23,8 +25,8 @@
     }
     
     @Override
-    public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws JsonMappingException{
-        ctxt.reportMappingException(_message);
+    public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
+        ctxt.reportInputMismatch(this, _message);
         return null;
     }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/FieldProperty.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/FieldProperty.java
index 756dda2..d8b38bb 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/FieldProperty.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/FieldProperty.java
@@ -5,7 +5,10 @@
 import java.lang.reflect.Field;
 
 import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+
 import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.NullValueProvider;
 import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
 import com.fasterxml.jackson.databind.introspect.AnnotatedField;
 import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
@@ -32,24 +35,33 @@
      */
     final protected transient Field _field;
 
+    /**
+     * @since 2.9
+     */
+    final protected boolean _skipNulls;
+
     public FieldProperty(BeanPropertyDefinition propDef, JavaType type,
             TypeDeserializer typeDeser, Annotations contextAnnotations, AnnotatedField field)
     {
         super(propDef, type, typeDeser, contextAnnotations);
         _annotated = field;
         _field = field.getAnnotated();
+        _skipNulls = NullsConstantProvider.isSkipper(_nullProvider);
     }
 
-    protected FieldProperty(FieldProperty src, JsonDeserializer<?> deser) {
-        super(src, deser);
+    protected FieldProperty(FieldProperty src, JsonDeserializer<?> deser,
+            NullValueProvider nva) {
+        super(src, deser, nva);
         _annotated = src._annotated;
         _field = src._field;
+        _skipNulls = NullsConstantProvider.isSkipper(nva);
     }
 
     protected FieldProperty(FieldProperty src, PropertyName newName) {
         super(src, newName);
         _annotated = src._annotated;
         _field = src._field;
+        _skipNulls = src._skipNulls;
     }
 
     /**
@@ -64,19 +76,25 @@
             throw new IllegalArgumentException("Missing field (broken JDK (de)serialization?)");
         }
         _field = f;
+        _skipNulls = src._skipNulls;
     }
-    
+
     @Override
-    public FieldProperty withName(PropertyName newName) {
+    public SettableBeanProperty withName(PropertyName newName) {
         return new FieldProperty(this, newName);
     }
-    
+
     @Override
-    public FieldProperty withValueDeserializer(JsonDeserializer<?> deser) {
+    public SettableBeanProperty withValueDeserializer(JsonDeserializer<?> deser) {
         if (_valueDeserializer == deser) {
             return this;
         }
-        return new FieldProperty(this, deser);
+        return new FieldProperty(this, deser, _nullProvider);
+    }
+
+    @Override
+    public SettableBeanProperty withNullProvider(NullValueProvider nva) {
+        return new FieldProperty(this, _valueDeserializer, nva);
     }
 
     @Override
@@ -108,7 +126,24 @@
     public void deserializeAndSet(JsonParser p,
     		DeserializationContext ctxt, Object instance) throws IOException
     {
-        Object value = deserialize(p, ctxt);
+        Object value;
+        if (p.hasToken(JsonToken.VALUE_NULL)) {
+            if (_skipNulls) {
+                return;
+            }
+            value = _nullProvider.getNullValue(ctxt);
+        } else if (_valueTypeDeserializer == null) {
+            value = _valueDeserializer.deserialize(p, ctxt);
+            // 04-May-2018, tatu: [databind#2023] Coercion from String (mostly) can give null
+            if (value == null) {
+                if (_skipNulls) {
+                    return;
+                }
+                value = _nullProvider.getNullValue(ctxt);
+            }
+        } else {
+            value = _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer);
+        }
         try {
             _field.set(instance, value);
         } catch (Exception e) {
@@ -120,7 +155,24 @@
     public Object deserializeSetAndReturn(JsonParser p,
     		DeserializationContext ctxt, Object instance) throws IOException
     {
-        Object value = deserialize(p, ctxt);
+        Object value;
+        if (p.hasToken(JsonToken.VALUE_NULL)) {
+            if (_skipNulls) {
+                return instance;
+            }
+            value = _nullProvider.getNullValue(ctxt);
+        } else if (_valueTypeDeserializer == null) {
+            value = _valueDeserializer.deserialize(p, ctxt);
+            // 04-May-2018, tatu: [databind#2023] Coercion from String (mostly) can give null
+            if (value == null) {
+                if (_skipNulls) {
+                    return instance;
+                }
+                value = _nullProvider.getNullValue(ctxt);
+            }
+        } else {
+            value = _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer);
+        }
         try {
             _field.set(instance, value);
         } catch (Exception e) {
@@ -128,9 +180,9 @@
         }
         return instance;
     }
-    
+
     @Override
-    public final void set(Object instance, Object value) throws IOException
+    public void set(Object instance, Object value) throws IOException
     {
         try {
             _field.set(instance, value);
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/InnerClassProperty.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/InnerClassProperty.java
index cac8057..bdc72dd 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/InnerClassProperty.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/InnerClassProperty.java
@@ -1,10 +1,10 @@
 package com.fasterxml.jackson.databind.deser.impl;
 
 import java.io.IOException;
-import java.lang.annotation.Annotation;
 import java.lang.reflect.Constructor;
 
 import com.fasterxml.jackson.core.*;
+
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
 import com.fasterxml.jackson.databind.introspect.*;
@@ -17,16 +17,11 @@
  * to regular implementation.
  */
 public final class InnerClassProperty
-    extends SettableBeanProperty
+    extends SettableBeanProperty.Delegating
 {
     private static final long serialVersionUID = 1L;
 
     /**
-     * Actual property that we use after value construction.
-     */
-    protected final SettableBeanProperty _delegate;
-
-    /**
      * Constructor used when deserializing this property.
      * Transient since there is no need to persist; only needed during
      * construction of objects.
@@ -42,7 +37,6 @@
             Constructor<?> ctor)
     {
         super(delegate);
-        _delegate = delegate;
         _creator = ctor;
     }
 
@@ -50,66 +44,24 @@
      * Constructor used with JDK Serialization; needed to handle transient
      * Constructor, wrap/unwrap in/out-of Annotated variant.
      */
-    protected InnerClassProperty(InnerClassProperty src, AnnotatedConstructor ann)
+    protected InnerClassProperty(SettableBeanProperty src, AnnotatedConstructor ann)
     {
         super(src);
-        _delegate = src._delegate;
         _annotated = ann;
         _creator = (_annotated == null) ? null : _annotated.getAnnotated();
         if (_creator == null) {
             throw new IllegalArgumentException("Missing constructor (broken JDK (de)serialization?)");
         }
     }
-    
-    protected InnerClassProperty(InnerClassProperty src, JsonDeserializer<?> deser)
-    {
-        super(src, deser);
-        _delegate = src._delegate.withValueDeserializer(deser);
-        _creator = src._creator;
-    }
-
-    protected InnerClassProperty(InnerClassProperty src, PropertyName newName) {
-        super(src, newName);
-        _delegate = src._delegate.withName(newName);
-        _creator = src._creator;
-    }
 
     @Override
-    public InnerClassProperty withName(PropertyName newName) {
-        return new InnerClassProperty(this, newName);
-    }
-
-    @Override
-    public InnerClassProperty withValueDeserializer(JsonDeserializer<?> deser) {
-        if (_valueDeserializer == deser) {
+    protected SettableBeanProperty withDelegate(SettableBeanProperty d) {
+        if (d == this.delegate) {
             return this;
         }
-        return new InnerClassProperty(this, deser);
+        return new InnerClassProperty(d, _creator);
     }
 
-    @Override
-    public void assignIndex(int index) { _delegate.assignIndex(index); }
-
-    @Override
-    public int getPropertyIndex() { return _delegate.getPropertyIndex(); }
-
-    @Override
-    public int getCreatorIndex() { return _delegate.getCreatorIndex(); }
-    
-    @Override
-    public void fixAccess(DeserializationConfig config) {
-        _delegate.fixAccess(config);
-    }
-
-    // // // BeanProperty impl
-    
-    @Override
-    public <A extends Annotation> A getAnnotation(Class<A> acls) {
-        return _delegate.getAnnotation(acls);
-    }
-
-    @Override public AnnotatedMember getMember() {  return _delegate.getMember(); }
-
     /*
     /**********************************************************
     /* Deserialization methods
@@ -117,44 +69,39 @@
      */
 
     @Override
-    public void deserializeAndSet(JsonParser jp, DeserializationContext ctxt, Object bean)
+    public void deserializeAndSet(JsonParser p, DeserializationContext ctxt, Object bean)
         throws IOException
     {
-        JsonToken t = jp.getCurrentToken();
+        JsonToken t = p.getCurrentToken();
         Object value;
         if (t == JsonToken.VALUE_NULL) {
             value = _valueDeserializer.getNullValue(ctxt);
         } else if (_valueTypeDeserializer != null) {
-            value = _valueDeserializer.deserializeWithType(jp, ctxt, _valueTypeDeserializer);
+            value = _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer);
         } else  { // the usual case
             try {
                 value = _creator.newInstance(bean);
             } catch (Exception e) {
-                ClassUtil.unwrapAndThrowAsIAE(e, "Failed to instantiate class "+_creator.getDeclaringClass().getName()+", problem: "+e.getMessage());
+                ClassUtil.unwrapAndThrowAsIAE(e, String.format(
+"Failed to instantiate class %s, problem: %s",
+_creator.getDeclaringClass().getName(), e.getMessage()));
                 value = null;
             }
-            _valueDeserializer.deserialize(jp, ctxt, value);
+            _valueDeserializer.deserialize(p, ctxt, value);
         }
         set(bean, value);
     }
 
     @Override
-    public Object deserializeSetAndReturn(JsonParser jp,
-    		DeserializationContext ctxt, Object instance)
+    public Object deserializeSetAndReturn(JsonParser p, DeserializationContext ctxt, Object instance)
         throws IOException
     {
-        return setAndReturn(instance, deserialize(jp, ctxt));
-    }
-    
-    @Override
-    public final void set(Object instance, Object value) throws IOException {
-        _delegate.set(instance, value);
+        return setAndReturn(instance, deserialize(p, ctxt));
     }
 
-    @Override
-    public Object setAndReturn(Object instance, Object value) throws IOException {
-        return _delegate.setAndReturn(instance, value);
-    }
+// these are fine with defaults
+//    public final void set(Object instance, Object value) throws IOException { }
+//    public Object setAndReturn(Object instance, Object value) throws IOException { }
 
     /*
     /**********************************************************
@@ -169,9 +116,9 @@
 
     Object writeReplace() {
         // need to construct a fake instance to support serialization
-        if (_annotated != null) {
-            return this;
+        if (_annotated == null) {
+            return new InnerClassProperty(this, new AnnotatedConstructor(null, _creator, null, null));
         }
-        return new InnerClassProperty(this, new AnnotatedConstructor(null, _creator, null, null));
+        return this;
     }
-}
\ No newline at end of file
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/JavaUtilCollectionsDeserializers.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/JavaUtilCollectionsDeserializers.java
new file mode 100644
index 0000000..ce27bbc
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/JavaUtilCollectionsDeserializers.java
@@ -0,0 +1,181 @@
+package com.fasterxml.jackson.databind.deser.impl;
+
+import java.util.*;
+
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.deser.std.StdDelegatingDeserializer;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+import com.fasterxml.jackson.databind.util.Converter;
+
+/**
+ * Helper class used to contain logic for deserializing "special" containers
+ * from {@code java.util.Collections} and {@code java.util.Arrays}. This is needed
+ * because they do not have usable no-arguments constructor: however, are easy enough
+ * to deserialize using delegating deserializer.
+ *
+ * @since 2.9.4
+ */
+public abstract class JavaUtilCollectionsDeserializers
+{
+    private final static int TYPE_SINGLETON_SET = 1;
+    private final static int TYPE_SINGLETON_LIST = 2;
+    private final static int TYPE_SINGLETON_MAP = 3;
+
+    private final static int TYPE_UNMODIFIABLE_SET = 4;
+    private final static int TYPE_UNMODIFIABLE_LIST = 5;
+    private final static int TYPE_UNMODIFIABLE_MAP = 6;
+
+    public final static int TYPE_AS_LIST = 7;
+
+    // 10-Jan-2018, tatu: There are a few "well-known" special containers in JDK too:
+
+    private final static Class<?> CLASS_AS_ARRAYS_LIST = Arrays.asList(null, null).getClass();
+
+    private final static Class<?> CLASS_SINGLETON_SET;
+    private final static Class<?> CLASS_SINGLETON_LIST;
+    private final static Class<?> CLASS_SINGLETON_MAP;
+
+    private final static Class<?> CLASS_UNMODIFIABLE_SET;
+    private final static Class<?> CLASS_UNMODIFIABLE_LIST;
+    private final static Class<?> CLASS_UNMODIFIABLE_MAP;
+
+    static {
+        Set<?> set = Collections.singleton(Boolean.TRUE);
+        CLASS_SINGLETON_SET = set.getClass();
+        CLASS_UNMODIFIABLE_SET = Collections.unmodifiableSet(set).getClass();
+
+        List<?> list = Collections.singletonList(Boolean.TRUE);
+        CLASS_SINGLETON_LIST = list.getClass();
+        CLASS_UNMODIFIABLE_LIST = Collections.unmodifiableList(list).getClass();
+
+        Map<?,?> map = Collections.singletonMap("a", "b");
+        CLASS_SINGLETON_MAP = map.getClass();
+        CLASS_UNMODIFIABLE_MAP = Collections.unmodifiableMap(map).getClass();
+    }
+
+    public static JsonDeserializer<?> findForCollection(DeserializationContext ctxt,
+            JavaType type)
+        throws JsonMappingException
+    {
+        JavaUtilCollectionsConverter conv;
+
+        // 10-Jan-2017, tatu: Some types from `java.util.Collections`/`java.util.Arrays` need bit of help...
+        if (type.hasRawClass(CLASS_AS_ARRAYS_LIST)) {
+            conv = converter(TYPE_AS_LIST, type, List.class);
+        } else if (type.hasRawClass(CLASS_SINGLETON_LIST)) {
+            conv = converter(TYPE_SINGLETON_LIST, type, List.class);
+        } else if (type.hasRawClass(CLASS_SINGLETON_SET)) {
+            conv = converter(TYPE_SINGLETON_SET, type, Set.class);
+        } else if (type.hasRawClass(CLASS_UNMODIFIABLE_LIST)) {
+            conv = converter(TYPE_UNMODIFIABLE_LIST, type, List.class);
+        } else if (type.hasRawClass(CLASS_UNMODIFIABLE_SET)) {
+            conv = converter(TYPE_UNMODIFIABLE_SET, type, Set.class);
+        } else {
+            return null;
+        }
+        return new StdDelegatingDeserializer<Object>(conv);
+    }
+
+    public static JsonDeserializer<?> findForMap(DeserializationContext ctxt,
+            JavaType type)
+        throws JsonMappingException
+    {
+        JavaUtilCollectionsConverter conv;
+
+        // 10-Jan-2017, tatu: Some types from `java.util.Collections`/`java.util.Arrays` need bit of help...
+        if (type.hasRawClass(CLASS_SINGLETON_MAP)) {
+            conv = converter(TYPE_SINGLETON_MAP, type, Map.class);
+        } else if (type.hasRawClass(CLASS_UNMODIFIABLE_MAP)) {
+            conv = converter(TYPE_UNMODIFIABLE_MAP, type, Map.class);
+        } else {
+            return null;
+        }
+        return new StdDelegatingDeserializer<Object>(conv);
+    }
+    
+    static JavaUtilCollectionsConverter converter(int kind,
+            JavaType concreteType, Class<?> rawSuper)
+    {
+        return new JavaUtilCollectionsConverter(kind, concreteType.findSuperType(rawSuper));
+    }
+
+    /**
+     * Implementation used for converting from various generic container
+     * types ({@link java.util.Set}, {@link java.util.List}, {@link java.util.Map})
+     * into more specific implementations accessible via {@code java.util.Collections}.
+     */
+    private static class JavaUtilCollectionsConverter implements Converter<Object,Object>
+    {
+        private final JavaType _inputType;
+
+        private final int _kind;
+
+        private JavaUtilCollectionsConverter(int kind, JavaType inputType) {
+            _inputType = inputType;
+            _kind = kind;
+        }
+        
+        @Override
+        public Object convert(Object value) {
+            if (value == null) { // is this legal to get?
+                return null;
+            }
+            
+            switch (_kind) {
+            case TYPE_SINGLETON_SET:
+                {
+                    Set<?> set = (Set<?>) value;
+                    _checkSingleton(set.size());
+                    return Collections.singleton(set.iterator().next());
+                }
+            case TYPE_SINGLETON_LIST:
+                {
+                    List<?> list = (List<?>) value;
+                    _checkSingleton(list.size());
+                    return Collections.singletonList(list.get(0));
+                }
+            case TYPE_SINGLETON_MAP:
+                {
+                    Map<?,?> map = (Map<?,?>) value;
+                    _checkSingleton(map.size());
+                    Map.Entry<?,?> entry = map.entrySet().iterator().next();
+                    return Collections.singletonMap(entry.getKey(), entry.getValue());
+                }
+
+            case TYPE_UNMODIFIABLE_SET:
+                return Collections.unmodifiableSet((Set<?>) value);
+            case TYPE_UNMODIFIABLE_LIST:
+                return Collections.unmodifiableList((List<?>) value);
+            case TYPE_UNMODIFIABLE_MAP:
+                return Collections.unmodifiableMap((Map<?,?>) value);
+
+            case TYPE_AS_LIST:
+            default:
+                // Here we do not actually care about impl type, just return List as-is:
+                return value;
+            }
+        }
+
+        @Override
+        public JavaType getInputType(TypeFactory typeFactory) {
+            return _inputType;
+        }
+
+        @Override
+        public JavaType getOutputType(TypeFactory typeFactory) {
+            // we don't actually care, so:
+            return _inputType;
+        }
+
+        private void _checkSingleton(int size) {
+            if (size != 1) {
+                // not the best error ever but... has to do
+                throw new IllegalArgumentException("Can not deserialize Singleton container from "+size+" entries");
+            }
+        }
+    }
+    
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/ManagedReferenceProperty.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ManagedReferenceProperty.java
index 21b465a..f0cbfda 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/ManagedReferenceProperty.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ManagedReferenceProperty.java
@@ -1,15 +1,12 @@
 package com.fasterxml.jackson.databind.deser.impl;
 
 import java.io.IOException;
-import java.lang.annotation.Annotation;
 import java.util.Collection;
 import java.util.Map;
 
 import com.fasterxml.jackson.core.JsonParser;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
-import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
-import com.fasterxml.jackson.databind.util.Annotations;
 
 /**
  * Wrapper property that is used to handle managed (forward) properties
@@ -17,7 +14,8 @@
  * then to back property.
  */
 public final class ManagedReferenceProperty
-    extends SettableBeanProperty
+    // Changed to extends delegating base class in 2.9
+    extends SettableBeanProperty.Delegating
 {
     private static final long serialVersionUID = 1L;
 
@@ -29,73 +27,31 @@
      */
     protected final boolean _isContainer;
 
-    protected final SettableBeanProperty _managedProperty;
-
     protected final SettableBeanProperty _backProperty;
 
     public ManagedReferenceProperty(SettableBeanProperty forward, String refName,
-            SettableBeanProperty backward, Annotations contextAnnotations, boolean isContainer)
+            SettableBeanProperty backward, boolean isContainer)
     {
-        super(forward.getFullName(), forward.getType(), forward.getWrapperName(),
-                forward.getValueTypeDeserializer(), contextAnnotations,
-                forward.getMetadata());
+        super(forward);
         _referenceName = refName;
-        _managedProperty = forward;
         _backProperty = backward;
         _isContainer = isContainer;
     }
 
-    protected ManagedReferenceProperty(ManagedReferenceProperty src, JsonDeserializer<?> deser)
-    {
-        super(src, deser);
-        _referenceName = src._referenceName;
-        _isContainer = src._isContainer;
-        _managedProperty = src._managedProperty;
-        _backProperty = src._backProperty;
-    }
-
-    protected ManagedReferenceProperty(ManagedReferenceProperty src, PropertyName newName) {
-        super(src, newName);
-        _referenceName = src._referenceName;
-        _isContainer = src._isContainer;
-        _managedProperty = src._managedProperty;
-        _backProperty = src._backProperty;
-    }
-
     @Override
-    public ManagedReferenceProperty withName(PropertyName newName) {
-        return new ManagedReferenceProperty(this, newName);
+    protected SettableBeanProperty withDelegate(SettableBeanProperty d) {
+        throw new IllegalStateException("Should never try to reset delegate");
     }
 
-    @Override
-    public ManagedReferenceProperty withValueDeserializer(JsonDeserializer<?> deser) {
-        if (_valueDeserializer == deser) {
-            return this;
-        }
-        return new ManagedReferenceProperty(this, deser);
-    }
- 
+    // need to override to ensure both get fixed
     @Override
     public void fixAccess(DeserializationConfig config) {
-        _managedProperty.fixAccess(config);
+        delegate.fixAccess(config);
         _backProperty.fixAccess(config);
     }
 
     /*
     /**********************************************************
-    /* BeanProperty impl
-    /**********************************************************
-     */
-
-    @Override
-    public <A extends Annotation> A getAnnotation(Class<A> acls) {
-        return _managedProperty.getAnnotation(acls);
-    }
-
-    @Override public AnnotatedMember getMember() {  return _managedProperty.getMember(); }
-
-    /*
-    /**********************************************************
     /* Overridden methods
     /**********************************************************
      */
@@ -103,7 +59,7 @@
     @Override
     public void deserializeAndSet(JsonParser p, DeserializationContext ctxt, Object instance)
             throws IOException {
-        set(instance, _managedProperty.deserialize(p, ctxt));
+        set(instance, delegate.deserialize(p, ctxt));
     }
 
     @Override
@@ -146,6 +102,6 @@
             }
         }
         // and then the forward reference itself
-        return _managedProperty.setAndReturn(instance, value);
+        return delegate.setAndReturn(instance, value);
 	}
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/MergingSettableBeanProperty.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/MergingSettableBeanProperty.java
new file mode 100644
index 0000000..db9fda7
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/MergingSettableBeanProperty.java
@@ -0,0 +1,133 @@
+package com.fasterxml.jackson.databind.deser.impl;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.deser.*;
+import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
+
+/**
+ * {@link SettableBeanProperty} implementation that will try to access value of
+ * the property first, and if non-null value found, pass that for update
+ * (using {@link com.fasterxml.jackson.databind.JsonDeserializer#deserialize(com.fasterxml.jackson.core.JsonParser, com.fasterxml.jackson.databind.DeserializationContext, Object)})
+ * instead of constructing a new value. This is necessary to support "merging" properties.
+ *<p>
+ * Note that there are many similarities to {@link SetterlessProperty}, which predates
+ * this variant; and that one is even used in cases where there is no mutator
+ * available.
+ *
+ * @since 2.9
+ */
+public class MergingSettableBeanProperty
+    extends SettableBeanProperty.Delegating
+{
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Member (field, method) used for accessing existing value.
+     */
+    protected final AnnotatedMember _accessor;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    protected MergingSettableBeanProperty(SettableBeanProperty delegate,
+            AnnotatedMember accessor)
+    {
+        super(delegate);
+        _accessor = accessor;
+    }
+
+    protected MergingSettableBeanProperty(MergingSettableBeanProperty src,
+            SettableBeanProperty delegate)
+    {
+        super(delegate);
+        _accessor = src._accessor;
+    }
+
+    public static MergingSettableBeanProperty construct(SettableBeanProperty delegate,
+            AnnotatedMember accessor)
+    {
+        return new MergingSettableBeanProperty(delegate, accessor);
+    }
+
+    @Override
+    protected SettableBeanProperty withDelegate(SettableBeanProperty d) {
+        return new MergingSettableBeanProperty(d, _accessor);
+    }
+
+    /*
+    /**********************************************************
+    /* Deserialization methods
+    /**********************************************************
+     */
+
+    @Override
+    public void deserializeAndSet(JsonParser p, DeserializationContext ctxt,
+            Object instance) throws IOException
+    {
+        Object oldValue = _accessor.getValue(instance);
+        Object newValue;
+        // 20-Oct-2016, tatu: Couple of possibilities of how to proceed; for
+        //    now, default to "normal" handling without merging
+        if (oldValue == null) {
+            newValue = delegate.deserialize(p, ctxt);
+        } else {
+            newValue = delegate.deserializeWith(p, ctxt, oldValue);
+        }
+        if (newValue != oldValue) {
+            // 18-Apr-2017, tatu: Null handling should occur within delegate, which may
+            //     set/skip/transform it, or throw an exception.
+            delegate.set(instance, newValue);
+        }
+    }
+
+    @Override
+    public Object deserializeSetAndReturn(JsonParser p,
+            DeserializationContext ctxt, Object instance) throws IOException
+    {
+        Object oldValue = _accessor.getValue(instance);
+        Object newValue;
+        // 20-Oct-2016, tatu: Couple of possibilities of how to proceed; for
+        //    now, default to "normal" handling without merging
+        if (oldValue == null) {
+            newValue = delegate.deserialize(p, ctxt);
+        } else {
+            newValue = delegate.deserializeWith(p, ctxt, oldValue);
+        }
+        // 23-Oct-2016, tatu: One possible complication here; should we always
+        //    try calling setter on builder? Presumably should not be required,
+        //    but may need to revise
+        if (newValue != oldValue) {
+            // 31-Oct-2016, tatu: Basically should just ignore as null can't really
+            //    contribute to merging.
+            if (newValue != null) {
+                return delegate.setAndReturn(instance, newValue);
+            }
+        }
+        return instance;
+    }
+
+    @Override
+    public void set(Object instance, Object value) throws IOException {
+        // 31-Oct-2016, tatu: Basically should just ignore as null can't really
+        //    contribute to merging.
+        if (value != null) {
+            delegate.set(instance, value);
+        }
+    }
+
+    @Override
+    public Object setAndReturn(Object instance, Object value) throws IOException {
+        // 31-Oct-2016, tatu: Basically should just ignore as null can't really
+        //    contribute to merging.
+        if (value != null) {
+            return delegate.setAndReturn(instance, value);
+        }
+        return instance;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/MethodProperty.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/MethodProperty.java
index bf4e0fe..aeeeba6 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/MethodProperty.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/MethodProperty.java
@@ -5,8 +5,9 @@
 import java.lang.reflect.Method;
 
 import com.fasterxml.jackson.core.JsonParser;
-
+import com.fasterxml.jackson.core.JsonToken;
 import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.NullValueProvider;
 import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
 import com.fasterxml.jackson.databind.introspect.*;
 import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
@@ -28,6 +29,11 @@
      * "regular" method-accessible properties.
      */
     protected final transient Method _setter;
+
+    /**
+     * @since 2.9
+     */
+    final protected boolean _skipNulls;
     
     public MethodProperty(BeanPropertyDefinition propDef,
             JavaType type, TypeDeserializer typeDeser,
@@ -36,18 +42,22 @@
         super(propDef, type, typeDeser, contextAnnotations);
         _annotated = method;
         _setter = method.getAnnotated();
+        _skipNulls = NullsConstantProvider.isSkipper(_nullProvider);
     }
 
-    protected MethodProperty(MethodProperty src, JsonDeserializer<?> deser) {
-        super(src, deser);
+    protected MethodProperty(MethodProperty src, JsonDeserializer<?> deser,
+            NullValueProvider nva) {
+        super(src, deser, nva);
         _annotated = src._annotated;
         _setter = src._setter;
+        _skipNulls = NullsConstantProvider.isSkipper(nva);
     }
 
     protected MethodProperty(MethodProperty src, PropertyName newName) {
         super(src, newName);
         _annotated = src._annotated;
         _setter = src._setter;
+        _skipNulls = src._skipNulls;
     }
 
     /**
@@ -57,19 +67,25 @@
         super(src);
         _annotated = src._annotated;
         _setter = m;
+        _skipNulls = src._skipNulls;
     }
-    
+
     @Override
-    public MethodProperty withName(PropertyName newName) {
+    public SettableBeanProperty withName(PropertyName newName) {
         return new MethodProperty(this, newName);
     }
     
     @Override
-    public MethodProperty withValueDeserializer(JsonDeserializer<?> deser) {
+    public SettableBeanProperty withValueDeserializer(JsonDeserializer<?> deser) {
         if (_valueDeserializer == deser) {
             return this;
         }
-        return new MethodProperty(this, deser);
+        return new MethodProperty(this, deser, _nullProvider);
+    }
+
+    @Override
+    public SettableBeanProperty withNullProvider(NullValueProvider nva) {
+        return new MethodProperty(this, _valueDeserializer, nva);
     }
 
     @Override
@@ -101,7 +117,24 @@
     public void deserializeAndSet(JsonParser p, DeserializationContext ctxt,
             Object instance) throws IOException
     {
-        Object value = deserialize(p, ctxt);
+        Object value;
+        if (p.hasToken(JsonToken.VALUE_NULL)) {
+            if (_skipNulls) {
+                return;
+            }
+            value = _nullProvider.getNullValue(ctxt);
+        } else if (_valueTypeDeserializer == null) {
+            value = _valueDeserializer.deserialize(p, ctxt);
+            // 04-May-2018, tatu: [databind#2023] Coercion from String (mostly) can give null
+            if (value == null) {
+                if (_skipNulls) {
+                    return;
+                }
+                value = _nullProvider.getNullValue(ctxt);
+            }
+        } else {
+            value = _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer);
+        }
         try {
             _setter.invoke(instance, value);
         } catch (Exception e) {
@@ -113,7 +146,24 @@
     public Object deserializeSetAndReturn(JsonParser p,
     		DeserializationContext ctxt, Object instance) throws IOException
     {
-        Object value = deserialize(p, ctxt);
+        Object value;
+        if (p.hasToken(JsonToken.VALUE_NULL)) {
+            if (_skipNulls) {
+                return instance;
+            }
+            value = _nullProvider.getNullValue(ctxt);
+        } else if (_valueTypeDeserializer == null) {
+            value = _valueDeserializer.deserialize(p, ctxt);
+            // 04-May-2018, tatu: [databind#2023] Coercion from String (mostly) can give null
+            if (value == null) {
+                if (_skipNulls) {
+                    return instance;
+                }
+                value = _nullProvider.getNullValue(ctxt);
+            }
+        } else {
+            value = _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer);
+        }
         try {
             Object result = _setter.invoke(instance, value);
             return (result == null) ? instance : result;
@@ -129,7 +179,7 @@
         try {
             _setter.invoke(instance, value);
         } catch (Exception e) {
-            // 15-Sep-2015, tatu: How coud we get a ref to JsonParser?
+            // 15-Sep-2015, tatu: How could we get a ref to JsonParser?
             _throwAsIOE(e, value);
         }
     }
@@ -141,7 +191,7 @@
             Object result = _setter.invoke(instance, value);
             return (result == null) ? instance : result;
         } catch (Exception e) {
-            // 15-Sep-2015, tatu: How coud we get a ref to JsonParser?
+            // 15-Sep-2015, tatu: How could we get a ref to JsonParser?
             _throwAsIOE(e, value);
             return null;
         }
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/NullsAsEmptyProvider.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/NullsAsEmptyProvider.java
new file mode 100644
index 0000000..51e385f
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/NullsAsEmptyProvider.java
@@ -0,0 +1,33 @@
+package com.fasterxml.jackson.databind.deser.impl;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.NullValueProvider;
+import com.fasterxml.jackson.databind.exc.InvalidNullException;
+import com.fasterxml.jackson.databind.util.AccessPattern;
+
+/**
+ * Simple {@link NullValueProvider} that will always throw a
+ * {@link InvalidNullException} when a null is encountered.
+ */
+public class NullsAsEmptyProvider
+    implements NullValueProvider, java.io.Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    protected final JsonDeserializer<?> _deserializer;
+
+    public NullsAsEmptyProvider(JsonDeserializer<?> deser) {
+        _deserializer = deser;
+    }
+
+    @Override
+    public AccessPattern getNullAccessPattern() {
+        return AccessPattern.DYNAMIC;
+    }
+
+    @Override
+    public Object getNullValue(DeserializationContext ctxt)
+            throws JsonMappingException {
+        return _deserializer.getEmptyValue(ctxt);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/NullsConstantProvider.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/NullsConstantProvider.java
new file mode 100644
index 0000000..5995a32
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/NullsConstantProvider.java
@@ -0,0 +1,79 @@
+package com.fasterxml.jackson.databind.deser.impl;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.NullValueProvider;
+import com.fasterxml.jackson.databind.exc.InvalidNullException;
+import com.fasterxml.jackson.databind.util.AccessPattern;
+
+/**
+ * Simple {@link NullValueProvider} that will always throw a
+ * {@link InvalidNullException} when a null is encountered.
+ */
+public class NullsConstantProvider
+    implements NullValueProvider, java.io.Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    private final static NullsConstantProvider SKIPPER = new NullsConstantProvider(null);
+
+    private final static NullsConstantProvider NULLER = new NullsConstantProvider(null);
+    
+    protected final Object _nullValue;
+
+    protected final AccessPattern _access;
+
+    protected NullsConstantProvider(Object nvl) {
+        _nullValue = nvl;
+        _access = (_nullValue == null) ? AccessPattern.ALWAYS_NULL
+                : AccessPattern.CONSTANT;
+    }
+
+    /**
+     * Static accessor for a stateless instance used as marker, to indicate
+     * that all input `null` values should be skipped (ignored), so that
+     * no corresponding property value is set (with POJOs), and no content
+     * values (array/Collection elements, Map entries) are added.
+     */
+    public static NullsConstantProvider skipper() {
+        return SKIPPER;
+    }
+
+    public static NullsConstantProvider nuller() {
+        return NULLER;
+    }
+
+    public static NullsConstantProvider forValue(Object nvl) {
+        if (nvl == null) {
+            return NULLER;
+        }
+        return new NullsConstantProvider(nvl);
+    }
+
+    /**
+     * Utility method that can be used to check if given null value provider
+     * is "skipper", marker provider that means that all input `null`s should
+     * be skipped (ignored), instead of converted
+     */
+    public static boolean isSkipper(NullValueProvider p) {
+        return (p == SKIPPER);
+    }
+
+    /**
+     * Utility method that can be used to check if given null value provider
+     * is "nuller", no-operation provider that will always simply return
+     * Java `null` for any and all input `null`s.
+     */
+    public static boolean isNuller(NullValueProvider p) {
+        return (p == NULLER);
+    }
+    
+    @Override
+    public AccessPattern getNullAccessPattern() {
+        return _access;
+    }
+    
+    @Override
+    public Object getNullValue(DeserializationContext ctxt) {
+        return _nullValue;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/NullsFailProvider.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/NullsFailProvider.java
new file mode 100644
index 0000000..ba870c7
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/NullsFailProvider.java
@@ -0,0 +1,44 @@
+package com.fasterxml.jackson.databind.deser.impl;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.NullValueProvider;
+import com.fasterxml.jackson.databind.exc.InvalidNullException;
+import com.fasterxml.jackson.databind.util.AccessPattern;
+
+/**
+ * Simple {@link NullValueProvider} that will always throw a
+ * {@link InvalidNullException} when a null is encountered.
+ */
+public class NullsFailProvider
+    implements NullValueProvider, java.io.Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    protected final PropertyName _name;
+    protected final JavaType _type;
+
+    protected NullsFailProvider(PropertyName name, JavaType type) {
+        _name = name;
+        _type = type;
+    }
+
+    public static NullsFailProvider constructForProperty(BeanProperty prop) {
+        return new NullsFailProvider(prop.getFullName(), prop.getType());
+    }
+
+    public static NullsFailProvider constructForRootValue(JavaType t) {
+        return new NullsFailProvider(null, t);
+    }
+
+    @Override
+    public AccessPattern getNullAccessPattern() {
+        // Must be called every time to effect the exception...
+        return AccessPattern.DYNAMIC;
+    }
+
+    @Override
+    public Object getNullValue(DeserializationContext ctxt)
+            throws JsonMappingException {
+        throw InvalidNullException.from(ctxt, _name, _type);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/ObjectIdReader.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ObjectIdReader.java
index 6e90e35..99b6e75 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/ObjectIdReader.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ObjectIdReader.java
@@ -4,8 +4,9 @@
 
 import com.fasterxml.jackson.annotation.ObjectIdGenerator;
 import com.fasterxml.jackson.annotation.ObjectIdResolver;
-import com.fasterxml.jackson.annotation.SimpleObjectIdResolver;
+
 import com.fasterxml.jackson.core.JsonParser;
+
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
 
@@ -55,13 +56,6 @@
         idProperty = idProp;
     }
 
-    @Deprecated // since 2.4
-    protected ObjectIdReader(JavaType t, PropertyName propName, ObjectIdGenerator<?> gen,
-            JsonDeserializer<?> deser, SettableBeanProperty idProp)
-    {
-        this(t,propName, gen, deser, idProp, new SimpleObjectIdResolver());
-    }
-
     /**
      * Factory method called by {@link com.fasterxml.jackson.databind.ser.std.BeanSerializerBase}
      * with the initial information based on standard settings for the type
@@ -74,14 +68,6 @@
         return new ObjectIdReader(idType, propName, generator, deser, idProp, resolver);
     }
 
-    @Deprecated // since 2.4
-    public static ObjectIdReader construct(JavaType idType, PropertyName propName,
-            ObjectIdGenerator<?> generator, JsonDeserializer<?> deser,
-            SettableBeanProperty idProp)
-    {
-        return construct(idType, propName, generator, deser, idProp, new SimpleObjectIdResolver());
-    }
-
     /*
     /**********************************************************
     /* API
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/ObjectIdReferenceProperty.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ObjectIdReferenceProperty.java
index 2034d91..f1b62ab 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/ObjectIdReferenceProperty.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ObjectIdReferenceProperty.java
@@ -5,6 +5,7 @@
 
 import com.fasterxml.jackson.core.JsonParser;
 import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.NullValueProvider;
 import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
 import com.fasterxml.jackson.databind.deser.UnresolvedForwardReference;
 import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId.Referring;
@@ -24,9 +25,10 @@
         _objectIdInfo = objectIdInfo;
     }
 
-    public ObjectIdReferenceProperty(ObjectIdReferenceProperty src, JsonDeserializer<?> deser)
+    public ObjectIdReferenceProperty(ObjectIdReferenceProperty src, JsonDeserializer<?> deser,
+            NullValueProvider nva)
     {
-        super(src, deser);
+        super(src, deser, nva);
         _forward = src._forward;
         _objectIdInfo = src._objectIdInfo;
     }
@@ -39,19 +41,24 @@
     }
 
     @Override
-    public SettableBeanProperty withValueDeserializer(JsonDeserializer<?> deser) {
-        if (_valueDeserializer == deser) {
-            return this;
-        }
-        return new ObjectIdReferenceProperty(this, deser);
-    }
-
-    @Override
     public SettableBeanProperty withName(PropertyName newName) {
         return new ObjectIdReferenceProperty(this, newName);
     }
 
     @Override
+    public SettableBeanProperty withValueDeserializer(JsonDeserializer<?> deser) {
+        if (_valueDeserializer == deser) {
+            return this;
+        }
+        return new ObjectIdReferenceProperty(this, deser, _nullProvider);
+    }
+
+    @Override
+    public SettableBeanProperty withNullProvider(NullValueProvider nva) {
+        return new ObjectIdReferenceProperty(this, _valueDeserializer, nva);
+    }
+    
+    @Override
     public void fixAccess(DeserializationConfig config) {
         if (_forward != null) {
             _forward.fixAccess(config);
@@ -86,7 +93,7 @@
         } catch (UnresolvedForwardReference reference) {
             boolean usingIdentityInfo = (_objectIdInfo != null) || (_valueDeserializer.getObjectIdReader() != null);
             if (!usingIdentityInfo) {
-                throw JsonMappingException.from(p, "Unresolved forward reference but no identity info.", reference);
+                throw JsonMappingException.from(p, "Unresolved forward reference but no identity info", reference);
             }
             reference.getRoid().appendReferring(new PropertyReferring(this, reference, _type.getRawClass(), instance));
             return null;
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/ObjectIdValueProperty.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ObjectIdValueProperty.java
index 4787fd0..6316e28 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/ObjectIdValueProperty.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ObjectIdValueProperty.java
@@ -29,9 +29,10 @@
         _objectIdReader = objectIdReader;
     }
 
-    protected ObjectIdValueProperty(ObjectIdValueProperty src, JsonDeserializer<?> deser)
+    protected ObjectIdValueProperty(ObjectIdValueProperty src, JsonDeserializer<?> deser,
+            NullValueProvider nva)
     {
-        super(src, deser);
+        super(src, deser, nva);
         _objectIdReader = src._objectIdReader;
     }
 
@@ -41,18 +42,23 @@
     }
 
     @Override
-    public ObjectIdValueProperty withName(PropertyName newName) {
+    public SettableBeanProperty withName(PropertyName newName) {
         return new ObjectIdValueProperty(this, newName);
     }
 
     @Override
-    public ObjectIdValueProperty withValueDeserializer(JsonDeserializer<?> deser) {
+    public SettableBeanProperty withValueDeserializer(JsonDeserializer<?> deser) {
         if (_valueDeserializer == deser) {
             return this;
         }
-        return new ObjectIdValueProperty(this, deser);
+        return new ObjectIdValueProperty(this, deser, _nullProvider);
     }
-    
+
+    @Override
+    public SettableBeanProperty withNullProvider(NullValueProvider nva) {
+        return new ObjectIdValueProperty(this, _valueDeserializer, nva);
+    }
+
     // // // BeanProperty impl
     
     @Override
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyBasedCreator.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyBasedCreator.java
index f0c4093..566b54f 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyBasedCreator.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyBasedCreator.java
@@ -1,14 +1,11 @@
 package com.fasterxml.jackson.databind.deser.impl;
 
 import java.io.IOException;
-import java.util.Collection;
-import java.util.HashMap;
+import java.util.*;
 
 import com.fasterxml.jackson.core.JsonParser;
 
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.JsonMappingException;
-import com.fasterxml.jackson.databind.MapperFeature;
+import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
 import com.fasterxml.jackson.databind.deser.ValueInstantiator;
 
@@ -53,9 +50,11 @@
     /**********************************************************
      */
 
-    protected PropertyBasedCreator(ValueInstantiator valueInstantiator,
+    protected PropertyBasedCreator(DeserializationContext ctxt,
+            ValueInstantiator valueInstantiator,
             SettableBeanProperty[] creatorProps,
-            boolean caseInsensitive)
+            boolean caseInsensitive,
+            boolean addAliases)
     {
         _valueInstantiator = valueInstantiator;
         if (caseInsensitive) {
@@ -66,31 +65,89 @@
         final int len = creatorProps.length;
         _propertyCount = len;
         _allProperties = new SettableBeanProperty[len];
+
+        // 26-Feb-2017, tatu: Let's start by aliases, so that there is no
+        //    possibility of accidental override of primary names
+        if (addAliases) {
+            final DeserializationConfig config = ctxt.getConfig();
+            for (SettableBeanProperty prop : creatorProps) {
+                // 22-Jan-2018, tatu: ignorable entries should be ignored, even if got aliases
+                if (!prop.isIgnorable()) {
+                    List<PropertyName> aliases = prop.findAliases(config);
+                    if (!aliases.isEmpty()) {
+                        for (PropertyName pn : aliases) {
+                            _propertyLookup.put(pn.getSimpleName(), prop);
+                        }
+                    }
+                }
+            }
+        }
         for (int i = 0; i < len; ++i) {
             SettableBeanProperty prop = creatorProps[i];
             _allProperties[i] = prop;
-            _propertyLookup.put(prop.getName(), prop);
+            // 22-Jan-2018, tatu: ignorable entries should be skipped
+            if (!prop.isIgnorable()) {
+                _propertyLookup.put(prop.getName(), prop);
+            }
         }
     }
 
     /**
-     * Factory method used for building actual instances: resolves deserializers
-     * and checks for "null values".
+     * Factory method used for building actual instances to be used with POJOS:
+     * resolves deserializers, checks for "null values".
+     *
+     * @since 2.9
      */
     public static PropertyBasedCreator construct(DeserializationContext ctxt,
-            ValueInstantiator valueInstantiator, SettableBeanProperty[] srcProps)
+            ValueInstantiator valueInstantiator, SettableBeanProperty[] srcCreatorProps,
+            BeanPropertyMap allProperties)
         throws JsonMappingException
     {
-        final int len = srcProps.length;
+        final int len = srcCreatorProps.length;
         SettableBeanProperty[] creatorProps = new SettableBeanProperty[len];
         for (int i = 0; i < len; ++i) {
-            SettableBeanProperty prop = srcProps[i];
+            SettableBeanProperty prop = srcCreatorProps[i];
             if (!prop.hasValueDeserializer()) {
                 prop = prop.withValueDeserializer(ctxt.findContextualValueDeserializer(prop.getType(), prop));
             }
             creatorProps[i] = prop;
         }
-        return new PropertyBasedCreator(valueInstantiator, creatorProps,
+        return new PropertyBasedCreator(ctxt, valueInstantiator, creatorProps,
+                allProperties.isCaseInsensitive(),
+                allProperties.hasAliases());
+    }
+
+    /**
+     * Factory method used for building actual instances to be used with types
+     * OTHER than POJOs.
+     * resolves deserializers and checks for "null values".
+     *
+     * @since 2.9
+     */
+    public static PropertyBasedCreator construct(DeserializationContext ctxt,
+            ValueInstantiator valueInstantiator, SettableBeanProperty[] srcCreatorProps,
+            boolean caseInsensitive)
+        throws JsonMappingException
+    {
+        final int len = srcCreatorProps.length;
+        SettableBeanProperty[] creatorProps = new SettableBeanProperty[len];
+        for (int i = 0; i < len; ++i) {
+            SettableBeanProperty prop = srcCreatorProps[i];
+            if (!prop.hasValueDeserializer()) {
+                prop = prop.withValueDeserializer(ctxt.findContextualValueDeserializer(prop.getType(), prop));
+            }
+            creatorProps[i] = prop;
+        }
+        return new PropertyBasedCreator(ctxt, valueInstantiator, creatorProps, 
+                caseInsensitive, false);
+    }
+
+    @Deprecated // since 2.9
+    public static PropertyBasedCreator construct(DeserializationContext ctxt,
+            ValueInstantiator valueInstantiator, SettableBeanProperty[] srcCreatorProps)
+        throws JsonMappingException
+    {
+        return construct(ctxt, valueInstantiator, srcCreatorProps,
                 ctxt.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES));
     }
 
@@ -158,7 +215,7 @@
 
     /**
      * Simple override of standard {@link java.util.HashMap} to support
-     * case-insensitive access to creator properties.
+     * case-insensitive access to creator properties
      *
      * @since 2.8.5
      */
@@ -168,8 +225,7 @@
 
         @Override
         public SettableBeanProperty get(Object key0) {
-            String key = (String) key0;
-            return super.get(key.toLowerCase());
+            return super.get(((String) key0).toLowerCase());
         }
 
         @Override
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer.java
index 73238b2..23f7503 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer.java
@@ -131,8 +131,8 @@
             value = _creatorParameters[prop.getCreatorIndex()] = _findMissing(prop);
         }
         if (value == null && _context.isEnabled(DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES)) {
-            throw _context.mappingException(
-                "Null value for creator property '%s'; DeserializationFeature.FAIL_ON_NULL_FOR_CREATOR_PARAMETERS enabled",
+            return _context.reportInputMismatch(prop,
+                "Null value for creator property '%s' (index %d); `DeserializationFeature.FAIL_ON_NULL_FOR_CREATOR_PARAMETERS` enabled",
                 prop.getName(), prop.getCreatorIndex());
         }
         return value;
@@ -169,13 +169,14 @@
 
         if (_context.isEnabled(DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES)) {
             for (int ix = 0; ix < props.length; ++ix) {
-              if (_creatorParameters[ix] == null) {
-                  _context.reportMappingException("Null value for creator property '%s'; DeserializationFeature.FAIL_ON_NULL_FOR_CREATOR_PARAMETERS enabled",
-                          props[ix].getName(), props[ix].getCreatorIndex());
-              }
+                if (_creatorParameters[ix] == null) {
+                    SettableBeanProperty prop = props[ix];
+                    _context.reportInputMismatch(prop.getType(),
+                            "Null value for creator property '%s' (index %d); `DeserializationFeature.FAIL_ON_NULL_FOR_CREATOR_PARAMETERS` enabled",
+                            prop.getName(), props[ix].getCreatorIndex());
+                }
             }
         }
-
         return _creatorParameters;
     }
 
@@ -189,11 +190,12 @@
         }
         // Second: required?
         if (prop.isRequired()) {
-            _context.reportMappingException("Missing required creator property '%s' (index %d)",
+            _context.reportInputMismatch(prop, "Missing required creator property '%s' (index %d)",
                     prop.getName(), prop.getCreatorIndex());
         }
         if (_context.isEnabled(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES)) {
-            _context.reportMappingException("Missing creator property '%s' (index %d); DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES enabled",
+            _context.reportInputMismatch(prop,
+                    "Missing creator property '%s' (index %d); `DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES` enabled",
                     prop.getName(), prop.getCreatorIndex());
         }
         // Third: default value
@@ -260,7 +262,6 @@
     {
         final int ix = prop.getCreatorIndex();
         _creatorParameters[ix] = value;
-
         if (_paramsSeenBig == null) {
             int old = _paramsSeen;
             int newValue = (old | (1 << ix));
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/SetterlessProperty.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/SetterlessProperty.java
index 8df84b6..2ce045b 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/SetterlessProperty.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/SetterlessProperty.java
@@ -8,6 +8,7 @@
 import com.fasterxml.jackson.core.JsonToken;
 
 import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.NullValueProvider;
 import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
 import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
 import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
@@ -40,8 +41,9 @@
         _getter = method.getAnnotated();
     }
 
-    protected SetterlessProperty(SetterlessProperty src, JsonDeserializer<?> deser) {
-        super(src, deser);
+    protected SetterlessProperty(SetterlessProperty src, JsonDeserializer<?> deser,
+            NullValueProvider nva) {
+        super(src, deser, nva);
         _annotated = src._annotated;
         _getter = src._getter;
     }
@@ -53,16 +55,21 @@
     }
 
     @Override
-    public SetterlessProperty withName(PropertyName newName) {
+    public SettableBeanProperty withName(PropertyName newName) {
         return new SetterlessProperty(this, newName);
     }
-    
+
     @Override
-    public SetterlessProperty withValueDeserializer(JsonDeserializer<?> deser) {
+    public SettableBeanProperty withValueDeserializer(JsonDeserializer<?> deser) {
         if (_valueDeserializer == deser) {
             return this;
         }
-        return new SetterlessProperty(this, deser);
+        return new SetterlessProperty(this, deser, _nullProvider);
+    }
+
+    @Override
+    public SettableBeanProperty withNullProvider(NullValueProvider nva) {
+        return new SetterlessProperty(this, _valueDeserializer, nva);
     }
 
     @Override
@@ -96,36 +103,32 @@
     {
         JsonToken t = p.getCurrentToken();
         if (t == JsonToken.VALUE_NULL) {
-            /* Hmmh. Is this a problem? We won't be setting anything, so it's
-             * equivalent of empty Collection/Map in this case
-             */
+            // Hmmh. Is this a problem? We won't be setting anything, so it's
+            // equivalent of empty Collection/Map in this case
             return;
         }
-
-        // For [#501] fix we need to implement this but:
+        // For [databind#501] fix we need to implement this but:
         if (_valueTypeDeserializer != null) {
-            ctxt.reportMappingException(
+            ctxt.reportBadDefinition(getType(), String.format(
                     "Problem deserializing 'setterless' property (\"%s\"): no way to handle typed deser with setterless yet",
-                    getName());
+                    getName()));
 //            return _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer);
         }
-        
         // Ok: then, need to fetch Collection/Map to modify:
         Object toModify;
         try {
-            toModify = _getter.invoke(instance);
+            toModify = _getter.invoke(instance, (Object[]) null);
         } catch (Exception e) {
             _throwAsIOE(p, e);
             return; // never gets here
         }
-        /* Note: null won't work, since we can't then inject anything
-         * in. At least that's not good in common case. However,
-         * theoretically the case where we get JSON null might
-         * be compatible. If so, implementation could be changed.
-         */
+        // Note: null won't work, since we can't then inject anything in. At least
+        // that's not good in common case. However, theoretically the case where
+        // we get JSON null might be compatible. If so, implementation could be changed.
         if (toModify == null) {
-            throw JsonMappingException.from(p,
-                    "Problem deserializing 'setterless' property '"+getName()+"': get method returned null");
+            ctxt.reportBadDefinition(getType(), String.format(
+                    "Problem deserializing 'setterless' property '%s': get method returned null",
+                    getName()));
         }
         _valueDeserializer.deserialize(p, ctxt, toModify);
     }
@@ -137,16 +140,16 @@
         deserializeAndSet(p, ctxt, instance);
         return instance;
     }
-    
+
     @Override
     public final void set(Object instance, Object value) throws IOException {
-        throw new UnsupportedOperationException("Should never call 'set' on setterless property");
+        throw new UnsupportedOperationException("Should never call `set()` on setterless property ('"+getName()+"')");
     }
 
     @Override
     public Object setAndReturn(Object instance, Object value) throws IOException
     {
         set(instance, value);
-        return null;
+        return instance;
     }
 }
\ No newline at end of file
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/TypeWrappedDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/TypeWrappedDeserializer.java
index 0f23fd1..8d5039d 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/TypeWrappedDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/TypeWrappedDeserializer.java
@@ -37,6 +37,11 @@
         return _deserializer.handledType();
     }
 
+    @Override // since 2.9
+    public Boolean supportsUpdate(DeserializationConfig config) {
+        return _deserializer.supportsUpdate(config);
+    }
+    
     @Override
     public JsonDeserializer<?> getDelegatee() {
         return _deserializer.getDelegatee();
@@ -58,13 +63,13 @@
     }
     
     @Override
-    public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException
+    public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
     {
-        return _deserializer.deserializeWithType(jp, ctxt, _typeDeserializer);
+        return _deserializer.deserializeWithType(p, ctxt, _typeDeserializer);
     }
 
     @Override
-    public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt,
+    public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
         TypeDeserializer typeDeserializer) throws IOException
     {
         // should never happen? (if it can, could call on that object)
@@ -72,12 +77,12 @@
     }
 
     @Override
-    public Object deserialize(JsonParser jp, DeserializationContext ctxt,
+    public Object deserialize(JsonParser p, DeserializationContext ctxt,
             Object intoValue) throws IOException
     {
         /* 01-Mar-2013, tatu: Hmmh. Tough call as to what to do... need
          *   to delegate, but will this work reliably? Let's just hope so:
          */
-        return _deserializer.deserialize(jp,  ctxt, intoValue);
+        return _deserializer.deserialize(p,  ctxt, intoValue);
     }
-}
\ No newline at end of file
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/ValueInjector.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ValueInjector.java
index 46e3bc9..148ba8f 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/ValueInjector.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ValueInjector.java
@@ -2,13 +2,8 @@
 
 import java.io.IOException;
 
-import com.fasterxml.jackson.databind.BeanProperty;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.JavaType;
-import com.fasterxml.jackson.databind.PropertyMetadata;
-import com.fasterxml.jackson.databind.PropertyName;
+import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
-import com.fasterxml.jackson.databind.util.Annotations;
 
 /**
  * Class that encapsulates details of value injection that occurs before
@@ -19,29 +14,33 @@
 public class ValueInjector
     extends BeanProperty.Std
 {
+    private static final long serialVersionUID = 1L;
+
     /**
      * Identifier used for looking up value to inject
      */
     protected final Object _valueId;
 
     public ValueInjector(PropertyName propName, JavaType type,
-            Annotations contextAnnotations, AnnotatedMember mutator,
-            Object valueId)
+            AnnotatedMember mutator, Object valueId)
     {
-        super(propName, type, null, contextAnnotations, mutator,
-                PropertyMetadata.STD_OPTIONAL);
+        super(propName, type, null, mutator, PropertyMetadata.STD_OPTIONAL);
         _valueId = valueId;
     }
 
-    @Deprecated // since 2.3
-    public ValueInjector(String propName, JavaType type,
-            Annotations contextAnnotations, AnnotatedMember mutator,
-            Object valueId)
+    /**
+     * @deprecated in 2.9 (remove from 3.0)
+     */
+    @Deprecated // see [databind#1835]
+    public ValueInjector(PropertyName propName, JavaType type,
+            com.fasterxml.jackson.databind.util.Annotations contextAnnotations, // removed from later versions
+            AnnotatedMember mutator, Object valueId)
     {
-        this(new PropertyName(propName), type, contextAnnotations, mutator, valueId);
+        this(propName, type, mutator, valueId);
     }
 
     public Object findValue(DeserializationContext context, Object beanInstance)
+        throws JsonMappingException
     {
         return context.findInjectableValue(_valueId, this, beanInstance);
     }
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/ArrayBlockingQueueDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/ArrayBlockingQueueDeserializer.java
index 0394068..c92c8a4 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/ArrayBlockingQueueDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/ArrayBlockingQueueDeserializer.java
@@ -5,8 +5,8 @@
 import java.util.concurrent.ArrayBlockingQueue;
 
 import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonToken;
 import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.NullValueProvider;
 import com.fasterxml.jackson.databind.deser.ValueInstantiator;
 import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
 
@@ -26,23 +26,24 @@
     /**********************************************************
      */
 
-     public ArrayBlockingQueueDeserializer(JavaType collectionType,
+     public ArrayBlockingQueueDeserializer(JavaType containerType,
             JsonDeserializer<Object> valueDeser, TypeDeserializer valueTypeDeser,
             ValueInstantiator valueInstantiator)
     {
-        super(collectionType, valueDeser, valueTypeDeser, valueInstantiator);
+        super(containerType, valueDeser, valueTypeDeser, valueInstantiator);
     }
 
     /**
      * Constructor used when creating contextualized instances.
      */
-     protected ArrayBlockingQueueDeserializer(JavaType collectionType,
+     protected ArrayBlockingQueueDeserializer(JavaType containerType,
             JsonDeserializer<Object> valueDeser, TypeDeserializer valueTypeDeser,
             ValueInstantiator valueInstantiator,
-            JsonDeserializer<Object> delegateDeser, Boolean unwrapSingle)
+            JsonDeserializer<Object> delegateDeser,
+            NullValueProvider nuller, Boolean unwrapSingle)
     {
-        super(collectionType, valueDeser, valueTypeDeser, valueInstantiator,
-                delegateDeser, unwrapSingle);
+        super(containerType, valueDeser, valueTypeDeser, valueInstantiator, delegateDeser,
+                nuller, unwrapSingle);
     }
 
     /**
@@ -59,16 +60,13 @@
     @Override
     @SuppressWarnings("unchecked")
     protected ArrayBlockingQueueDeserializer withResolved(JsonDeserializer<?> dd,
-            JsonDeserializer<?> vd, TypeDeserializer vtd, Boolean unwrapSingle)
+            JsonDeserializer<?> vd, TypeDeserializer vtd,
+            NullValueProvider nuller, Boolean unwrapSingle)
     {
-        if ((dd == _delegateDeserializer) && (vd == _valueDeserializer) && (vtd == _valueTypeDeserializer)
-                && (_unwrapSingle == unwrapSingle)) {
-            return this;
-        }
-        return new ArrayBlockingQueueDeserializer(_collectionType,
+        return new ArrayBlockingQueueDeserializer(_containerType,
                 (JsonDeserializer<Object>) vd, vtd,
-                _valueInstantiator, (JsonDeserializer<Object>) dd, unwrapSingle);
-                
+                _valueInstantiator, (JsonDeserializer<Object>) dd,
+                nuller, unwrapSingle);
     }
 
     /*
@@ -76,63 +74,35 @@
     /* JsonDeserializer API
     /**********************************************************
      */
-    
-    @SuppressWarnings("unchecked")
+
     @Override
-    public Collection<Object> deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException
+    protected Collection<Object> createDefaultInstance(DeserializationContext ctxt)
+        throws IOException
     {
-        if (_delegateDeserializer != null) {
-            return (Collection<Object>) _valueInstantiator.createUsingDelegate(ctxt,
-                    _delegateDeserializer.deserialize(jp, ctxt));
-        }
-        if (jp.getCurrentToken() == JsonToken.VALUE_STRING) {
-            String str = jp.getText();
-            if (str.length() == 0) {
-                return (Collection<Object>) _valueInstantiator.createFromString(ctxt, str);
-            }
-        }
-        return deserialize(jp, ctxt, null);
+        // 07-Nov-2016, tatu: Important: cannot create using default ctor (one
+        //    does not exist); and also need to know exact size. Hence, return
+        //    null from here
+        return null;
     }
 
     @Override
-    public Collection<Object> deserialize(JsonParser jp, DeserializationContext ctxt, Collection<Object> result0) throws IOException
+    public Collection<Object> deserialize(JsonParser p, DeserializationContext ctxt,
+            Collection<Object> result0) throws IOException
     {
-        // Ok: must point to START_ARRAY (or equivalent)
-        if (!jp.isExpectedStartArrayToken()) {
-            return handleNonArray(jp, ctxt, new ArrayBlockingQueue<Object>(1));
-        }
-        ArrayList<Object> tmp = new ArrayList<Object>();
-        
-        JsonDeserializer<Object> valueDes = _valueDeserializer;
-        JsonToken t;
-        final TypeDeserializer typeDeser = _valueTypeDeserializer;
-
-        try {
-            while ((t = jp.nextToken()) != JsonToken.END_ARRAY) {
-                Object value;
-                
-                if (t == JsonToken.VALUE_NULL) {
-                    value = valueDes.getNullValue(ctxt);
-                } else if (typeDeser == null) {
-                    value = valueDes.deserialize(jp, ctxt);
-                } else {
-                    value = valueDes.deserializeWithType(jp, ctxt, typeDeser);
-                }
-                tmp.add(value);
-            }
-        } catch (Exception e) {
-            throw JsonMappingException.wrapWithPath(e, tmp, tmp.size());
-        }
         if (result0 != null) {
-            result0.addAll(tmp);
-            return result0;
+            return super.deserialize(p, ctxt, result0);
         }
-        return new ArrayBlockingQueue<Object>(tmp.size(), false, tmp);
+        // Ok: must point to START_ARRAY (or equivalent)
+        if (!p.isExpectedStartArrayToken()) {
+            return handleNonArray(p, ctxt, new ArrayBlockingQueue<Object>(1));
+        }
+        result0 = super.deserialize(p, ctxt, new ArrayList<Object>());
+        return new ArrayBlockingQueue<Object>(result0.size(), false, result0);
     }
 
     @Override
-    public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException {
+    public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException {
         // In future could check current token... for now this should be enough:
-        return typeDeserializer.deserializeTypedFromArray(jp, ctxt);
+        return typeDeserializer.deserializeTypedFromArray(p, ctxt);
     }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/AtomicReferenceDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/AtomicReferenceDeserializer.java
index 8faa423..916e266 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/AtomicReferenceDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/AtomicReferenceDeserializer.java
@@ -3,6 +3,7 @@
 import java.util.concurrent.atomic.AtomicReference;
 
 import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.ValueInstantiator;
 import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
 
 public class AtomicReferenceDeserializer
@@ -16,15 +17,13 @@
     /**********************************************************
      */
 
-    @Deprecated // since 2.8
-    public AtomicReferenceDeserializer(JavaType fullType) {
-        this(fullType, null, null);
-    }
-
-    public AtomicReferenceDeserializer(JavaType fullType,
+    /**
+     * @since 2.9
+     */
+    public AtomicReferenceDeserializer(JavaType fullType, ValueInstantiator inst,
             TypeDeserializer typeDeser, JsonDeserializer<?> deser)
     {
-        super(fullType, typeDeser, deser);
+        super(fullType, inst, typeDeser, deser);
     }
 
     /*
@@ -35,42 +34,38 @@
 
     @Override
     public AtomicReferenceDeserializer withResolved(TypeDeserializer typeDeser, JsonDeserializer<?> valueDeser) {
-        return new AtomicReferenceDeserializer(_fullType, typeDeser, valueDeser);
+        return new AtomicReferenceDeserializer(_fullType, _valueInstantiator,
+                typeDeser, valueDeser);
     }
-
     @Override
     public AtomicReference<Object> getNullValue(DeserializationContext ctxt) {
         return new AtomicReference<Object>();
     }
 
     @Override
+    public Object getEmptyValue(DeserializationContext ctxt) {
+        return new AtomicReference<Object>();
+    }
+    
+    @Override
     public AtomicReference<Object> referenceValue(Object contents) {
         return new AtomicReference<Object>(contents);
     }
 
-    /*
     @Override
-    public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
-            TypeDeserializer typeDeser) throws IOException
-    {
-        final JsonToken t = p.getCurrentToken();
-        if (t == JsonToken.VALUE_NULL) { // can this actually happen?
-            return getNullValue(ctxt);
-        }
-        // 22-Oct-2015, tatu: This handling is probably not needed (or is wrong), but
-        //   could be result of older (pre-2.7) Jackson trying to serialize natural types.
-        //  Because of this, let's allow for now, unless proven problematic
-        if ((t != null) && t.isScalarValue()) {
-            return deserialize(p, ctxt);
-        }
-        // 19-Apr-2016, tatu: Alas, due to there not really being anything for AtomicReference
-        //   itself, need to just ignore `typeDeser`, use TypeDeserializer we do have for contents
-        //   and it might just work.
-
-        if (_valueTypeDeserializer == null) {
-            return deserialize(p, ctxt);
-        }
-        return new AtomicReference<Object>(_valueTypeDeserializer.deserializeTypedFromAny(p, ctxt));
+    public Object getReferenced(AtomicReference<Object> reference) {
+        return reference.get();
     }
-    */
+
+    @Override // since 2.9
+    public AtomicReference<Object> updateReference(AtomicReference<Object> reference, Object contents) {
+        reference.set(contents);
+        return reference;
+    }
+
+    @Override // since 2.9
+    public Boolean supportsUpdate(DeserializationConfig config) {
+        // yes; regardless of value deserializer reference itself may be updated
+        return Boolean.TRUE;
+    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/CollectionDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/CollectionDeserializer.java
index c3cb45f..c255d89 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/CollectionDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/CollectionDeserializer.java
@@ -4,15 +4,16 @@
 import java.util.*;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
+
 import com.fasterxml.jackson.core.*;
+
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
-import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
-import com.fasterxml.jackson.databind.deser.UnresolvedForwardReference;
-import com.fasterxml.jackson.databind.deser.ValueInstantiator;
+import com.fasterxml.jackson.databind.deser.*;
 import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId.Referring;
 import com.fasterxml.jackson.databind.deser.std.ContainerDeserializerBase;
 import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.util.ClassUtil;
 
 /**
  * Basic serializer that can take JSON "Array" structure and
@@ -32,8 +33,6 @@
 
     // // Configuration
 
-    protected final JavaType _collectionType;
-    
     /**
      * Value deserializer.
      */
@@ -55,15 +54,6 @@
      */
     protected final JsonDeserializer<Object> _delegateDeserializer;
 
-    /**
-     * Specific override for this instance (from proper, or global per-type overrides)
-     * to indicate whether single value may be taken to mean an unwrapped one-element array
-     * or not. If null, left to global defaults.
-     *
-     * @since 2.7
-     */
-    protected final Boolean _unwrapSingle;
-
     // NOTE: no PropertyBasedCreator, as JSON Arrays have no properties
 
     /*
@@ -80,25 +70,24 @@
             JsonDeserializer<Object> valueDeser,
             TypeDeserializer valueTypeDeser, ValueInstantiator valueInstantiator)
     {
-        this(collectionType, valueDeser, valueTypeDeser, valueInstantiator, null, null);
+        this(collectionType, valueDeser, valueTypeDeser, valueInstantiator, null, null, null);
     }
 
     /**
      * Constructor used when creating contextualized instances.
+     *
+     * @since 2.9
      */
     protected CollectionDeserializer(JavaType collectionType,
             JsonDeserializer<Object> valueDeser, TypeDeserializer valueTypeDeser,
-            ValueInstantiator valueInstantiator,
-            JsonDeserializer<Object> delegateDeser,
-            Boolean unwrapSingle)
+            ValueInstantiator valueInstantiator, JsonDeserializer<Object> delegateDeser,
+            NullValueProvider nuller, Boolean unwrapSingle)
     {
-        super(collectionType);
-        _collectionType = collectionType;
+        super(collectionType, nuller, unwrapSingle);
         _valueDeserializer = valueDeser;
         _valueTypeDeserializer = valueTypeDeser;
         _valueInstantiator = valueInstantiator;
         _delegateDeserializer = delegateDeser;
-        _unwrapSingle = unwrapSingle;
     }
 
     /**
@@ -107,42 +96,28 @@
      */
     protected CollectionDeserializer(CollectionDeserializer src)
     {
-        super(src._collectionType);
-        _collectionType = src._collectionType;
+        super(src);
         _valueDeserializer = src._valueDeserializer;
         _valueTypeDeserializer = src._valueTypeDeserializer;
         _valueInstantiator = src._valueInstantiator;
         _delegateDeserializer = src._delegateDeserializer;
-        _unwrapSingle = src._unwrapSingle;
     }
 
     /**
      * Fluent-factory method call to construct contextual instance.
      *
-     * @since 2.7
+     * @since 2.9
      */
     @SuppressWarnings("unchecked")
     protected CollectionDeserializer withResolved(JsonDeserializer<?> dd,
             JsonDeserializer<?> vd, TypeDeserializer vtd,
-            Boolean unwrapSingle)
+            NullValueProvider nuller, Boolean unwrapSingle)
     {
-        if ((dd == _delegateDeserializer) && (vd == _valueDeserializer) && (vtd == _valueTypeDeserializer)
-                && (_unwrapSingle == unwrapSingle)) {
-            return this;
-        }
-        return new CollectionDeserializer(_collectionType,
+//if (true) throw new Error();
+        return new CollectionDeserializer(_containerType,
                 (JsonDeserializer<Object>) vd, vtd,
-                _valueInstantiator, (JsonDeserializer<Object>) dd, unwrapSingle);
-    }
-
-    /**
-     * @deprecated Since 2.7 as it does not pass `unwrapSingle`
-     */
-    @Deprecated // since 2.7 -- will not retain "unwrapSingle" setting
-    protected CollectionDeserializer withResolved(JsonDeserializer<?> dd,
-            JsonDeserializer<?> vd, TypeDeserializer vtd)
-    {
-        return withResolved(dd, vd, vtd, _unwrapSingle);
+                _valueInstantiator, (JsonDeserializer<Object>) dd,
+                nuller, unwrapSingle);
     }
 
     // Important: do NOT cache if polymorphic values
@@ -176,17 +151,19 @@
             if (_valueInstantiator.canCreateUsingDelegate()) {
                 JavaType delegateType = _valueInstantiator.getDelegateType(ctxt.getConfig());
                 if (delegateType == null) {
-                    throw new IllegalArgumentException("Invalid delegate-creator definition for "+_collectionType
-                            +": value instantiator ("+_valueInstantiator.getClass().getName()
-                            +") returned true for 'canCreateUsingDelegate()', but null for 'getDelegateType()'");
+                    ctxt.reportBadDefinition(_containerType, String.format(
+"Invalid delegate-creator definition for %s: value instantiator (%s) returned true for 'canCreateUsingDelegate()', but null for 'getDelegateType()'",
+_containerType,
+                            _valueInstantiator.getClass().getName()));
                 }
                 delegateDeser = findDeserializer(ctxt, delegateType, property);
             } else if (_valueInstantiator.canCreateUsingArrayDelegate()) {
                 JavaType delegateType = _valueInstantiator.getArrayDelegateType(ctxt.getConfig());
                 if (delegateType == null) {
-                    throw new IllegalArgumentException("Invalid array-delegate-creator definition for "+_collectionType
-                            +": value instantiator ("+_valueInstantiator.getClass().getName()
-                            +") returned true for 'canCreateUsingArrayDelegate()', but null for 'getArrayDelegateType()'");
+                    ctxt.reportBadDefinition(_containerType, String.format(
+"Invalid delegate-creator definition for %s: value instantiator (%s) returned true for 'canCreateUsingArrayDelegate()', but null for 'getArrayDelegateType()'",
+                            _containerType,
+                            _valueInstantiator.getClass().getName()));
                 }
                 delegateDeser = findDeserializer(ctxt, delegateType, property);
             }
@@ -201,7 +178,7 @@
         
         // May have a content converter
         valueDeser = findConvertingContentDeserializer(ctxt, property, valueDeser);
-        final JavaType vt = _collectionType.getContentType();
+        final JavaType vt = _containerType.getContentType();
         if (valueDeser == null) {
             valueDeser = ctxt.findContextualValueDeserializer(vt, property);
         } else { // if directly assigned, probably not yet contextual, so:
@@ -212,7 +189,17 @@
         if (valueTypeDeser != null) {
             valueTypeDeser = valueTypeDeser.forProperty(property);
         }
-        return withResolved(delegateDeser, valueDeser, valueTypeDeser, unwrapSingle);
+        NullValueProvider nuller = findContentNullProvider(ctxt, property, valueDeser);
+        if ( (unwrapSingle != _unwrapSingle)
+                || (nuller != _nullProvider)
+                || (delegateDeser != _delegateDeserializer)
+                || (valueDeser != _valueDeserializer)
+                || (valueTypeDeser != _valueTypeDeserializer)
+        ) {
+            return withResolved(delegateDeser, valueDeser, valueTypeDeser,
+                    nuller, unwrapSingle);
+        }
+        return this;
     }
 
     /*
@@ -222,21 +209,21 @@
      */
 
     @Override
-    public JavaType getContentType() {
-        return _collectionType.getContentType();
-    }
-
-    @Override
     public JsonDeserializer<Object> getContentDeserializer() {
         return _valueDeserializer;
     }
-    
+
+    @Override
+    public ValueInstantiator getValueInstantiator() {
+        return _valueInstantiator;
+    }
+
     /*
     /**********************************************************
     /* JsonDeserializer API
     /**********************************************************
      */
-    
+
     @SuppressWarnings("unchecked")
     @Override
     public Collection<Object> deserialize(JsonParser p, DeserializationContext ctxt)
@@ -246,19 +233,28 @@
             return (Collection<Object>) _valueInstantiator.createUsingDelegate(ctxt,
                     _delegateDeserializer.deserialize(p, ctxt));
         }
-        /* Empty String may be ok; bit tricky to check, however, since
-         * there is also possibility of "auto-wrapping" of single-element arrays.
-         * Hence we only accept empty String here.
-         */
+        // Empty String may be ok; bit tricky to check, however, since
+        // there is also possibility of "auto-wrapping" of single-element arrays.
+        // Hence we only accept empty String here.
         if (p.hasToken(JsonToken.VALUE_STRING)) {
             String str = p.getText();
             if (str.length() == 0) {
                 return (Collection<Object>) _valueInstantiator.createFromString(ctxt, str);
             }
         }
-        return deserialize(p, ctxt, (Collection<Object>) _valueInstantiator.createUsingDefault(ctxt));
+        return deserialize(p, ctxt, createDefaultInstance(ctxt));
     }
 
+    /**
+     * @since 2.9
+     */
+    @SuppressWarnings("unchecked")
+    protected Collection<Object> createDefaultInstance(DeserializationContext ctxt)
+        throws IOException
+    {
+        return (Collection<Object>) _valueInstantiator.createUsingDefault(ctxt);
+    }
+    
     @Override
     public Collection<Object> deserialize(JsonParser p, DeserializationContext ctxt,
             Collection<Object> result)
@@ -272,38 +268,36 @@
         p.setCurrentValue(result);
 
         JsonDeserializer<Object> valueDes = _valueDeserializer;
+        // Let's offline handling of values with Object Ids (simplifies code here)
+        if (valueDes.getObjectIdReader() != null) {
+            return _deserializeWithObjectId(p, ctxt, result);
+        }
         final TypeDeserializer typeDeser = _valueTypeDeserializer;
-        CollectionReferringAccumulator referringAccumulator =
-            (valueDes.getObjectIdReader() == null) ? null :
-                new CollectionReferringAccumulator(_collectionType.getContentType().getRawClass(), result);
-
         JsonToken t;
         while ((t = p.nextToken()) != JsonToken.END_ARRAY) {
             try {
                 Object value;
                 if (t == JsonToken.VALUE_NULL) {
-                    value = valueDes.getNullValue(ctxt);
+                    if (_skipNullValues) {
+                        continue;
+                    }
+                    value = _nullProvider.getNullValue(ctxt);
                 } else if (typeDeser == null) {
                     value = valueDes.deserialize(p, ctxt);
                 } else {
                     value = valueDes.deserializeWithType(p, ctxt, typeDeser);
                 }
-                if (referringAccumulator != null) {
-                    referringAccumulator.add(value);
-                } else {
-                    result.add(value);
-                }
+                result.add(value);
+
+                /* 17-Dec-2017, tatu: should not occur at this level...
             } catch (UnresolvedForwardReference reference) {
-                if (referringAccumulator == null) {
-                    throw JsonMappingException
-                            .from(p, "Unresolved forward reference but no identity info", reference);
-                }
-                Referring ref = referringAccumulator.handleUnresolvedReference(reference);
-                reference.getRoid().appendReferring(ref);
+                throw JsonMappingException
+                    .from(p, "Unresolved forward reference but no identity info", reference);
+                */
             } catch (Exception e) {
                 boolean wrap = (ctxt == null) || ctxt.isEnabled(DeserializationFeature.WRAP_EXCEPTIONS);
-                if (!wrap && e instanceof RuntimeException) {
-                    throw (RuntimeException)e;
+                if (!wrap) {
+                    ClassUtil.throwIfRTE(e);
                 }
                 throw JsonMappingException.wrapWithPath(e, result, result.size());
             }
@@ -335,7 +329,7 @@
                 ((_unwrapSingle == null) &&
                         ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY));
         if (!canWrap) {
-            return (Collection<Object>) ctxt.handleUnexpectedToken(_collectionType.getRawClass(), p);
+            return (Collection<Object>) ctxt.handleUnexpectedToken(_containerType.getRawClass(), p);
         }
         JsonDeserializer<Object> valueDes = _valueDeserializer;
         final TypeDeserializer typeDeser = _valueTypeDeserializer;
@@ -345,7 +339,11 @@
 
         try {
             if (t == JsonToken.VALUE_NULL) {
-                value = valueDes.getNullValue(ctxt);
+                // 03-Feb-2017, tatu: Hmmh. I wonder... let's try skipping here, too
+                if (_skipNullValues) {
+                    return result;
+                }
+                value = _nullProvider.getNullValue(ctxt);
             } else if (typeDeser == null) {
                 value = valueDes.deserialize(p, ctxt);
             } else {
@@ -359,7 +357,56 @@
         return result;
     }
 
-    public final static class CollectionReferringAccumulator {
+    protected Collection<Object> _deserializeWithObjectId(JsonParser p, DeserializationContext ctxt,
+            Collection<Object> result)
+        throws IOException
+    {
+        // Ok: must point to START_ARRAY (or equivalent)
+        if (!p.isExpectedStartArrayToken()) {
+            return handleNonArray(p, ctxt, result);
+        }
+        // [databind#631]: Assign current value, to be accessible by custom serializers
+        p.setCurrentValue(result);
+
+        final JsonDeserializer<Object> valueDes = _valueDeserializer;
+        final TypeDeserializer typeDeser = _valueTypeDeserializer;
+        CollectionReferringAccumulator referringAccumulator =
+                new CollectionReferringAccumulator(_containerType.getContentType().getRawClass(), result);
+
+        JsonToken t;
+        while ((t = p.nextToken()) != JsonToken.END_ARRAY) {
+            try {
+                Object value;
+                if (t == JsonToken.VALUE_NULL) {
+                    if (_skipNullValues) {
+                        continue;
+                    }
+                    value = _nullProvider.getNullValue(ctxt);
+                } else if (typeDeser == null) {
+                    value = valueDes.deserialize(p, ctxt);
+                } else {
+                    value = valueDes.deserializeWithType(p, ctxt, typeDeser);
+                }
+                referringAccumulator.add(value);
+            } catch (UnresolvedForwardReference reference) {
+                Referring ref = referringAccumulator.handleUnresolvedReference(reference);
+                reference.getRoid().appendReferring(ref);
+            } catch (Exception e) {
+                boolean wrap = (ctxt == null) || ctxt.isEnabled(DeserializationFeature.WRAP_EXCEPTIONS);
+                if (!wrap) {
+                    ClassUtil.throwIfRTE(e);
+                }
+                throw JsonMappingException.wrapWithPath(e, result, result.size());
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Helper class for dealing with Object Id references for values contained in
+     * collections being deserialized.
+     */
+    public static class CollectionReferringAccumulator {
         private final Class<?> _elementType;
         private final Collection<Object> _result;
 
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/ContainerDeserializerBase.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/ContainerDeserializerBase.java
index 251a51c..123cf6b 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/ContainerDeserializerBase.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/ContainerDeserializerBase.java
@@ -3,10 +3,14 @@
 import java.io.IOException;
 import java.lang.reflect.InvocationTargetException;
 
-import com.fasterxml.jackson.databind.JavaType;
-import com.fasterxml.jackson.databind.JsonDeserializer;
-import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.NullValueProvider;
 import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
+import com.fasterxml.jackson.databind.deser.ValueInstantiator;
+import com.fasterxml.jackson.databind.deser.impl.NullsConstantProvider;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+import com.fasterxml.jackson.databind.util.AccessPattern;
+import com.fasterxml.jackson.databind.util.ClassUtil;
 
 /**
  * Intermediate base deserializer class that adds more shared accessor
@@ -15,9 +19,64 @@
 @SuppressWarnings("serial")
 public abstract class ContainerDeserializerBase<T>
     extends StdDeserializer<T>
+    implements ValueInstantiator.Gettable // since 2.9
 {
-    protected ContainerDeserializerBase(JavaType selfType) {
+    protected final JavaType _containerType;
+
+    /**
+     * Handler we need for dealing with nulls.
+     *
+     * @since 2.9
+     */
+    protected final NullValueProvider _nullProvider;
+
+    /**
+     * Specific override for this instance (from proper, or global per-type overrides)
+     * to indicate whether single value may be taken to mean an unwrapped one-element array
+     * or not. If null, left to global defaults.
+     *
+     * @since 2.9 (demoted from sub-classes where added in 2.7)
+     */
+    protected final Boolean _unwrapSingle;
+
+    /**
+     * Marker flag set if the <code>_nullProvider</code> indicates that all null
+     * content values should be skipped (instead of being possibly converted).
+     *
+     * @since 2.9
+     */
+    protected final boolean _skipNullValues;
+
+    protected ContainerDeserializerBase(JavaType selfType,
+            NullValueProvider nuller, Boolean unwrapSingle) {
         super(selfType);
+        _containerType = selfType;
+        _unwrapSingle = unwrapSingle;
+        _nullProvider = nuller;
+        _skipNullValues = NullsConstantProvider.isSkipper(nuller);
+    }
+
+    protected ContainerDeserializerBase(JavaType selfType) {
+        this(selfType, null, null);
+    }
+
+    /**
+     * @since 2.9
+     */
+    protected ContainerDeserializerBase(ContainerDeserializerBase<?> base) {
+        this(base, base._nullProvider, base._unwrapSingle);
+    }
+
+    /**
+     * @since 2.9
+     */
+    protected ContainerDeserializerBase(ContainerDeserializerBase<?> base,
+            NullValueProvider nuller, Boolean unwrapSingle) {
+        super(base._containerType);
+        _containerType = base._containerType;
+        _nullProvider = nuller;
+        _unwrapSingle = unwrapSingle;
+        _skipNullValues = NullsConstantProvider.isSkipper(nuller);
     }
 
     /*
@@ -26,16 +85,25 @@
     /**********************************************************
      */
 
+    @Override // since 2.9
+    public JavaType getValueType() { return _containerType; }
+    
+    @Override // since 2.9
+    public Boolean supportsUpdate(DeserializationConfig config) {
+        return Boolean.TRUE;
+    }
+
     @Override
     public SettableBeanProperty findBackReference(String refName) {
         JsonDeserializer<Object> valueDeser = getContentDeserializer();
         if (valueDeser == null) {
-            throw new IllegalArgumentException("Can not handle managed/back reference '"+refName
-                    +"': type: container deserializer of type "+getClass().getName()+" returned null for 'getContentDeserializer()'");
+            throw new IllegalArgumentException(String.format(
+                    "Cannot handle managed/back reference '%s': type: container deserializer of type %s returned null for 'getContentDeserializer()'",
+                    refName, getClass().getName()));
         }
         return valueDeser.findBackReference(refName);
     }
-    
+
     /*
     /**********************************************************
     /* Extended API
@@ -46,13 +114,48 @@
      * Accessor for declared type of contained value elements; either exact
      * type, or one of its supertypes.
      */
-    public abstract JavaType getContentType();
+    public JavaType getContentType() {
+        if (_containerType == null) {
+            return TypeFactory.unknownType(); // should never occur but...
+        }
+        return _containerType.getContentType();
+    }
 
     /**
      * Accesor for deserializer use for deserializing content values.
      */
     public abstract JsonDeserializer<Object> getContentDeserializer();
 
+    /**
+     * @since 2.9
+     */
+    @Override
+    public ValueInstantiator getValueInstantiator() {
+        return null;
+    }
+
+    @Override // since 2.9
+    public AccessPattern getEmptyAccessPattern() {
+        // 02-Feb-2017, tatu: Empty containers are usually constructed as needed
+        //   and may not be shared; for some deserializers this may be further refined.
+        return AccessPattern.DYNAMIC;
+    }
+    
+    @Override // since 2.9
+    public Object getEmptyValue(DeserializationContext ctxt) throws JsonMappingException {
+        ValueInstantiator vi = getValueInstantiator();
+        if (vi == null || !vi.canCreateUsingDefault()) {
+            JavaType type = getValueType();
+            ctxt.reportBadDefinition(type,
+                    String.format("Cannot create empty instance of %s, no default Creator", type));
+        }
+        try {
+            return vi.createUsingDefault(ctxt);
+        } catch (IOException e) {
+            return ClassUtil.throwAsMappingException(ctxt, e);
+        }
+    }
+
     /*
     /**********************************************************
     /* Shared methods for sub-classes
@@ -62,24 +165,20 @@
     /**
      * Helper method called by various Map(-like) deserializers.
      */
-    protected void wrapAndThrow(Throwable t, Object ref, String key) throws IOException
+    protected <BOGUS> BOGUS wrapAndThrow(Throwable t, Object ref, String key) throws IOException
     {
         // to handle StackOverflow:
         while (t instanceof InvocationTargetException && t.getCause() != null) {
             t = t.getCause();
         }
         // Errors and "plain" IOExceptions to be passed as is
-        if (t instanceof Error) {
-            throw (Error) t;
-        }
+        ClassUtil.throwIfError(t);
         // ... except for mapping exceptions
         if (t instanceof IOException && !(t instanceof JsonMappingException)) {
             throw (IOException) t;
         }
         // for [databind#1141]
-        if (key == null) {
-            key = "N/A";
-        }
-        throw JsonMappingException.wrapWithPath(t, ref, key);
+        throw JsonMappingException.wrapWithPath(t, ref,
+                ClassUtil.nonNull(key, "N/A"));
     }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/DateDeserializers.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/DateDeserializers.java
index 4a840bc..14bddb3 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/DateDeserializers.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/DateDeserializers.java
@@ -1,20 +1,20 @@
 package com.fasterxml.jackson.databind.deser.std;
 
 import java.io.IOException;
+import java.lang.reflect.Constructor;
 import java.sql.Timestamp;
 import java.text.*;
 import java.util.*;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
+
 import com.fasterxml.jackson.core.JsonParser;
 import com.fasterxml.jackson.core.JsonToken;
-import com.fasterxml.jackson.databind.BeanProperty;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.DeserializationFeature;
-import com.fasterxml.jackson.databind.JsonDeserializer;
-import com.fasterxml.jackson.databind.JsonMappingException;
+
+import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
 import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
+import com.fasterxml.jackson.databind.util.ClassUtil;
 import com.fasterxml.jackson.databind.util.StdDateFormat;
 
 /**
@@ -59,7 +59,7 @@
         }
         return null;
     }
-    
+
     /*
     /**********************************************************
     /* Intermediate class for Date-based ones
@@ -80,7 +80,7 @@
          * Let's also keep format String for reference, to use for error messages
          */
         protected final String _formatString;
-        
+
         protected DateBasedDeserializer(Class<?> clz) {
             super(clz);
             _customFormat = null;
@@ -97,54 +97,87 @@
         protected abstract DateBasedDeserializer<T> withDateFormat(DateFormat df, String formatStr);
 
         @Override
-        public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)
+        public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
+                BeanProperty property)
            throws JsonMappingException
         {
-            if (property != null) {
-                JsonFormat.Value format = findFormatOverrides(ctxt, property,
-                        this.handledType());
-                if (format != null) {
-                    TimeZone tz = format.getTimeZone();
-                    // First: fully custom pattern?
-                    if (format.hasPattern()) {
-                        final String pattern = format.getPattern();
+            final JsonFormat.Value format = findFormatOverrides(ctxt, property,
+                    handledType());
+
+            if (format != null) {
+                TimeZone tz = format.getTimeZone();
+                final Boolean lenient = format.getLenient();
+
+                // First: fully custom pattern?
+                if (format.hasPattern()) {
+                    final String pattern = format.getPattern();
+                    final Locale loc = format.hasLocale() ? format.getLocale() : ctxt.getLocale();
+                    SimpleDateFormat df = new SimpleDateFormat(pattern, loc);
+                    if (tz == null) {
+                        tz = ctxt.getTimeZone();
+                    }
+                    df.setTimeZone(tz);
+                    if (lenient != null) {
+                        df.setLenient(lenient);
+                    }
+                    return withDateFormat(df, pattern);
+                }
+                // But if not, can still override timezone
+                if (tz != null) {
+                    DateFormat df = ctxt.getConfig().getDateFormat();
+                    // one shortcut: with our custom format, can simplify handling a bit
+                    if (df.getClass() == StdDateFormat.class) {
                         final Locale loc = format.hasLocale() ? format.getLocale() : ctxt.getLocale();
-                        SimpleDateFormat df = new SimpleDateFormat(pattern, loc);
-                        if (tz == null) {
-                            tz = ctxt.getTimeZone();
+                        StdDateFormat std = (StdDateFormat) df;
+                        std = std.withTimeZone(tz);
+                        std = std.withLocale(loc);
+                        if (lenient != null) {
+                            std = std.withLenient(lenient);
                         }
+                        df = std;
+                    } else {
+                        // otherwise need to clone, re-set timezone:
+                        df = (DateFormat) df.clone();
                         df.setTimeZone(tz);
-                        return withDateFormat(df, pattern);
-                    }
-                    // But if not, can still override timezone
-                    if (tz != null) {
-                        DateFormat df = ctxt.getConfig().getDateFormat();
-                        // one shortcut: with our custom format, can simplify handling a bit
-                        if (df.getClass() == StdDateFormat.class) {
-                            final Locale loc = format.hasLocale() ? format.getLocale() : ctxt.getLocale();
-                            StdDateFormat std = (StdDateFormat) df;
-                            std = std.withTimeZone(tz);
-                            std = std.withLocale(loc);
-                            df = std;
-                        } else {
-                            // otherwise need to clone, re-set timezone:
-                            df = (DateFormat) df.clone();
-                            df.setTimeZone(tz);
+                        if (lenient != null) {
+                            df.setLenient(lenient);
                         }
-                        return withDateFormat(df, _formatString);
                     }
+                    return withDateFormat(df, _formatString);
+                }
+                // or maybe even just leniency?
+                if (lenient != null) {
+                    DateFormat df = ctxt.getConfig().getDateFormat();
+                    String pattern = _formatString;
+                    // one shortcut: with our custom format, can simplify handling a bit
+                    if (df.getClass() == StdDateFormat.class) {
+                        StdDateFormat std = (StdDateFormat) df;
+                        std = std.withLenient(lenient);
+                        df = std;
+                        pattern = std.toPattern();
+                    } else {
+                        // otherwise need to clone,
+                        df = (DateFormat) df.clone();
+                        df.setLenient(lenient);
+                        if (df instanceof SimpleDateFormat) {
+                            ((SimpleDateFormat) df).toPattern();
+                        }
+                    }
+                    if (pattern == null) {
+                        pattern = "[unknown]";
+                    }
+                    return withDateFormat(df, pattern);
                 }
             }
             return this;
         }
-        
+
         @Override
         protected java.util.Date _parseDate(JsonParser p, DeserializationContext ctxt)
             throws IOException
         {
             if (_customFormat != null) {
-                JsonToken t = p.getCurrentToken();
-                if (t == JsonToken.VALUE_STRING) {
+                if (p.hasToken(JsonToken.VALUE_STRING)) {
                     String str = p.getText().trim();
                     if (str.length() == 0) {
                         return (Date) getEmptyValue(ctxt);
@@ -158,16 +191,6 @@
                         }
                     }
                 }
-                // [databind#381]
-                if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
-                    p.nextToken();
-                    final Date parsed = _parseDate(p, ctxt);
-                    t = p.nextToken();
-                    if (t != JsonToken.END_ARRAY) {
-                        handleMissingEndArrayForSingle(p, ctxt);
-                    }            
-                    return parsed;            
-                }
             }
             return super._parseDate(p, ctxt);
         }
@@ -185,29 +208,32 @@
         /**
          * We may know actual expected type; if so, it will be
          * used for instantiation.
+         *
+         * @since 2.9
          */
-        protected final Class<? extends Calendar> _calendarClass;
-        
+        protected final Constructor<Calendar> _defaultCtor;
+
         public CalendarDeserializer() {
             super(Calendar.class);
-            _calendarClass = null;
+            _defaultCtor = null;
         }
 
+        @SuppressWarnings("unchecked")
         public CalendarDeserializer(Class<? extends Calendar> cc) {
             super(cc);
-            _calendarClass = cc;
+            _defaultCtor = (Constructor<Calendar>) ClassUtil.findConstructor(cc, false);
         }
 
         public CalendarDeserializer(CalendarDeserializer src, DateFormat df, String formatString) {
             super(src, df, formatString);
-            _calendarClass = src._calendarClass;
+            _defaultCtor = src._defaultCtor;
         }
 
         @Override
         protected CalendarDeserializer withDateFormat(DateFormat df, String formatString) {
             return new CalendarDeserializer(this, df, formatString);
         }
-        
+
         @Override
         public Calendar deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
         {
@@ -215,11 +241,11 @@
             if (d == null) {
                 return null;
             }
-            if (_calendarClass == null) {
+            if (_defaultCtor == null) {
                 return ctxt.constructCalendar(d);
             }
             try {
-                Calendar c = _calendarClass.newInstance();            
+                Calendar c = _defaultCtor.newInstance();            
                 c.setTimeInMillis(d.getTime());
                 TimeZone tz = ctxt.getTimeZone();
                 if (tz != null) {
@@ -227,7 +253,7 @@
                 }
                 return c;
             } catch (Exception e) {
-                return (Calendar) ctxt.handleInstantiationProblem(_calendarClass, d, e);
+                return (Calendar) ctxt.handleInstantiationProblem(handledType(), d, e);
             }
         }
     }
@@ -255,8 +281,8 @@
         }
         
         @Override
-        public java.util.Date deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
-            return _parseDate(jp, ctxt);
+        public java.util.Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
+            return _parseDate(p, ctxt);
         }
     }
 
@@ -278,8 +304,8 @@
         }
         
         @Override
-        public java.sql.Date deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
-            Date d = _parseDate(jp, ctxt);
+        public java.sql.Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
+            Date d = _parseDate(p, ctxt);
             return (d == null) ? null : new java.sql.Date(d.getTime());
         }
     }
@@ -304,9 +330,9 @@
         }
         
         @Override
-        public java.sql.Timestamp deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException
+        public java.sql.Timestamp deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
         {
-            Date d = _parseDate(jp, ctxt);
+            Date d = _parseDate(p, ctxt);
             return (d == null) ? null : new Timestamp(d.getTime());
         }
     }
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/DelegatingDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/DelegatingDeserializer.java
index 0c14ca2..f436878 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/DelegatingDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/DelegatingDeserializer.java
@@ -4,11 +4,11 @@
 import java.util.Collection;
 
 import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.deser.*;
 import com.fasterxml.jackson.databind.deser.impl.ObjectIdReader;
 import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.util.AccessPattern;
 
 /**
  * Base class that simplifies implementations of {@link JsonDeserializer}s
@@ -32,22 +32,19 @@
     /**********************************************************************
      */
 
-    public DelegatingDeserializer(JsonDeserializer<?> delegatee)
+    public DelegatingDeserializer(JsonDeserializer<?> d)
     {
-        super(_figureType(delegatee));
-        _delegatee = delegatee;
+        super(d.handledType());
+        _delegatee = d;
     }
 
-    protected abstract JsonDeserializer<?> newDelegatingInstance(JsonDeserializer<?> newDelegatee);
+    /*
+    /**********************************************************************
+    /* Abstract methods to implement
+    /**********************************************************************
+     */
     
-    private static Class<?> _figureType(JsonDeserializer<?> deser)
-    {
-        Class<?> cls = deser.handledType();
-        if (cls != null) {
-            return cls;
-        }
-        return Object.class;
-    }
+    protected abstract JsonDeserializer<?> newDelegatingInstance(JsonDeserializer<?> newDelegatee);
     
     /*
     /**********************************************************************
@@ -76,23 +73,13 @@
         return newDelegatingInstance(del);
     }
 
-    /**
-     * @deprecated Since 2.3, use {@link #newDelegatingInstance} instead
-     */
-    @Deprecated
-    protected JsonDeserializer<?> _createContextual(DeserializationContext ctxt,
-            BeanProperty property, JsonDeserializer<?> newDelegatee)
+    @Override
+    public JsonDeserializer<?> replaceDelegatee(JsonDeserializer<?> delegatee)
     {
-        if (newDelegatee == _delegatee) {
+        if (delegatee == _delegatee) {
             return this;
         }
-        return newDelegatingInstance(newDelegatee);
-    }
-
-    @Override
-    public SettableBeanProperty findBackReference(String logicalName) {
-        // [Issue#253]: Hope this works....
-        return _delegatee.findBackReference(logicalName);
+        return newDelegatingInstance(delegatee);
     }
 
     /*
@@ -102,27 +89,27 @@
      */
 
     @Override
-    public Object deserialize(JsonParser jp, DeserializationContext ctxt)
-        throws IOException, JsonProcessingException
+    public Object deserialize(JsonParser p, DeserializationContext ctxt)
+        throws IOException
     {
-        return _delegatee.deserialize(jp,  ctxt);
+        return _delegatee.deserialize(p,  ctxt);
     }
 
     @SuppressWarnings("unchecked")
     @Override
-    public Object deserialize(JsonParser jp, DeserializationContext ctxt,
+    public Object deserialize(JsonParser p, DeserializationContext ctxt,
             Object intoValue)
-        throws IOException, JsonProcessingException
+        throws IOException
     {
-        return ((JsonDeserializer<Object>)_delegatee).deserialize(jp, ctxt, intoValue);
+        return ((JsonDeserializer<Object>)_delegatee).deserialize(p, ctxt, intoValue);
     }
 
     @Override
-    public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt,
+    public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
             TypeDeserializer typeDeserializer)
-        throws IOException, JsonProcessingException
+        throws IOException
     {
-        return _delegatee.deserializeWithType(jp, ctxt, typeDeserializer);
+        return _delegatee.deserializeWithType(p, ctxt, typeDeserializer);
     }
 
     /*
@@ -132,12 +119,27 @@
      */
 
     @Override
-    public JsonDeserializer<?> replaceDelegatee(JsonDeserializer<?> delegatee)
-    {
-        if (delegatee == _delegatee) {
-            return this;
-        }
-        return newDelegatingInstance(delegatee);
+    public boolean isCachable() { return _delegatee.isCachable(); }
+
+    @Override // since 2.9
+    public Boolean supportsUpdate(DeserializationConfig config) {
+        return _delegatee.supportsUpdate(config);
+    }
+
+    @Override
+    public JsonDeserializer<?> getDelegatee() {
+        return _delegatee;
+    }
+
+    @Override
+    public SettableBeanProperty findBackReference(String logicalName) {
+        // [databind#253]: Hope this works....
+        return _delegatee.findBackReference(logicalName);
+    }
+
+    @Override
+    public AccessPattern getNullAccessPattern() {
+        return _delegatee.getNullAccessPattern();
     }
 
     @Override
@@ -151,26 +153,8 @@
     }
 
     @Override
-    @Deprecated // remove in 2.7
-    public Object getNullValue() { return _delegatee.getNullValue(); }
-
-    // Remove in 2.7
-    @Override
-    @Deprecated // remove in 2.7
-    public Object getEmptyValue() { return _delegatee.getEmptyValue(); }
-
-    
-    @Override
     public Collection<Object> getKnownPropertyNames() { return _delegatee.getKnownPropertyNames(); }
-    
-    @Override
-    public boolean isCachable() { return _delegatee.isCachable(); }
 
     @Override
     public ObjectIdReader getObjectIdReader() { return _delegatee.getObjectIdReader(); }
-
-    @Override
-    public JsonDeserializer<?> getDelegatee() {
-        return _delegatee;
-    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumDeserializer.java
index c3ad93e..c697e1c 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumDeserializer.java
@@ -2,9 +2,13 @@
 
 import java.io.IOException;
 
+import com.fasterxml.jackson.annotation.JsonFormat;
+
 import com.fasterxml.jackson.core.*;
+
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
+import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
 import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
 import com.fasterxml.jackson.databind.deser.ValueInstantiator;
 import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
@@ -19,6 +23,7 @@
 @JacksonStdImpl // was missing until 2.6
 public class EnumDeserializer
     extends StdScalarDeserializer<Object>
+    implements ContextualDeserializer
 {
     private static final long serialVersionUID = 1L;
 
@@ -41,16 +46,42 @@
      * @since 2.7.3
      */
     protected CompactStringObjectMap _lookupByToString;
-    
-    public EnumDeserializer(EnumResolver byNameResolver)
+
+    protected final Boolean _caseInsensitive;
+
+    /**
+     * @since 2.9
+     */
+    public EnumDeserializer(EnumResolver byNameResolver, Boolean caseInsensitive)
     {
         super(byNameResolver.getEnumClass());
         _lookupByName = byNameResolver.constructLookup();
         _enumsByIndex = byNameResolver.getRawEnums();
         _enumDefaultValue = byNameResolver.getDefaultValue();
+        _caseInsensitive = caseInsensitive;
     }
 
     /**
+     * @since 2.9
+     */
+    protected EnumDeserializer(EnumDeserializer base, Boolean caseInsensitive)
+    {
+        super(base);
+        _lookupByName = base._lookupByName;
+        _enumsByIndex = base._enumsByIndex;
+        _enumDefaultValue = base._enumDefaultValue;
+        _caseInsensitive = caseInsensitive;
+    }
+
+    /**
+     * @deprecated Since 2.9
+     */
+    @Deprecated
+    public EnumDeserializer(EnumResolver byNameResolver) {
+        this(byNameResolver, null);
+    }
+    
+    /**
      * @deprecated Since 2.8
      */
     @Deprecated
@@ -98,6 +129,28 @@
         return new FactoryBasedEnumDeserializer(enumClass, factory);
     }
 
+    /**
+     * @since 2.9
+     */
+    public EnumDeserializer withResolved(Boolean caseInsensitive) {
+        if (_caseInsensitive == caseInsensitive) {
+            return this;
+        }
+        return new EnumDeserializer(this, caseInsensitive);
+    }
+    
+    @Override // since 2.9
+    public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
+            BeanProperty property) throws JsonMappingException
+    {
+        Boolean caseInsensitive = findFormatFeature(ctxt, property, handledType(),
+                JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);
+        if (caseInsensitive == null) {
+            caseInsensitive = _caseInsensitive;
+        }
+        return withResolved(caseInsensitive);
+    }
+
     /*
     /**********************************************************
     /* Default JsonDeserializer implementation
@@ -167,17 +220,30 @@
             if (ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)) {
                 return getEmptyValue(ctxt);
             }
-        } else if (!ctxt.isEnabled(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS)) {
-            // [databind#149]: Allow use of 'String' indexes as well -- unless prohibited (as per above)
-            char c = name.charAt(0);
-            if (c >= '0' && c <= '9') {
-                try {
-                    int index = Integer.parseInt(name);
-                    if (index >= 0 && index < _enumsByIndex.length) {
-                        return _enumsByIndex[index];
+        } else {
+            // [databind#1313]: Case insensitive enum deserialization
+            if (Boolean.TRUE.equals(_caseInsensitive)) {
+                Object match = lookup.findCaseInsensitive(name);
+                if (match != null) {
+                    return match;
+                }
+            } else if (!ctxt.isEnabled(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS)) {
+                // [databind#149]: Allow use of 'String' indexes as well -- unless prohibited (as per above)
+                char c = name.charAt(0);
+                if (c >= '0' && c <= '9') {
+                    try {
+                        int index = Integer.parseInt(name);
+                        if (!ctxt.isEnabled(MapperFeature.ALLOW_COERCION_OF_SCALARS)) {
+                            return ctxt.handleWeirdStringValue(_enumClass(), name,
+"value looks like quoted Enum index, but `MapperFeature.ALLOW_COERCION_OF_SCALARS` prevents use"
+                                    );
+                        }
+                        if (index >= 0 && index < _enumsByIndex.length) {
+                            return _enumsByIndex[index];
+                        }
+                    } catch (NumberFormatException e) {
+                        // fine, ignore, was not an integer
                     }
-                } catch (NumberFormatException e) {
-                    // fine, ignore, was not an integer
                 }
             }
         }
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumMapDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumMapDeserializer.java
index d227834..f61b17c 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumMapDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumMapDeserializer.java
@@ -6,7 +6,14 @@
 import com.fasterxml.jackson.core.*;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
+import com.fasterxml.jackson.databind.deser.NullValueProvider;
+import com.fasterxml.jackson.databind.deser.ResolvableDeserializer;
+import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
+import com.fasterxml.jackson.databind.deser.ValueInstantiator;
+import com.fasterxml.jackson.databind.deser.impl.PropertyBasedCreator;
+import com.fasterxml.jackson.databind.deser.impl.PropertyValueBuffer;
 import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.util.ClassUtil;
 
 /**
  * Deserializer for {@link EnumMap} values.
@@ -17,12 +24,10 @@
 @SuppressWarnings({ "unchecked", "rawtypes" }) 
 public class EnumMapDeserializer
     extends ContainerDeserializerBase<EnumMap<?,?>>
-    implements ContextualDeserializer
+    implements ContextualDeserializer, ResolvableDeserializer
 {
     private static final long serialVersionUID = 1;
 
-    protected final JavaType _mapType;
-    
     protected final Class<?> _enumClass;
 
     protected KeyDeserializer _keyDeserializer;
@@ -34,31 +39,127 @@
      * is the type deserializer that can handle it
      */
     protected final TypeDeserializer _valueTypeDeserializer;
+
+    // // Instance construction settings:
     
+    /**
+     * @since 2.9
+     */
+    protected final ValueInstantiator _valueInstantiator;
+
+    /**
+     * Deserializer that is used iff delegate-based creator is
+     * to be used for deserializing from JSON Object.
+     */
+    protected JsonDeserializer<Object> _delegateDeserializer;
+
+    /**
+     * If the Map is to be instantiated using non-default constructor
+     * or factory method
+     * that takes one or more named properties as argument(s),
+     * this creator is used for instantiation.
+     */
+    protected PropertyBasedCreator _propertyBasedCreator;    
+
     /*
     /**********************************************************
     /* Life-cycle
     /**********************************************************
      */
 
-    public EnumMapDeserializer(JavaType mapType, KeyDeserializer keyDeserializer, JsonDeserializer<?> valueDeser, TypeDeserializer valueTypeDeser)
+    /**
+     * @since 2.9
+     */
+    public EnumMapDeserializer(JavaType mapType, ValueInstantiator valueInst,
+            KeyDeserializer keyDeser, JsonDeserializer<?> valueDeser, TypeDeserializer vtd,
+            NullValueProvider nuller)
     {
-        super(mapType);
-        _mapType = mapType;
+        super(mapType, nuller, null);
         _enumClass = mapType.getKeyType().getRawClass();
-        _keyDeserializer = keyDeserializer;
+        _keyDeserializer = keyDeser;
         _valueDeserializer = (JsonDeserializer<Object>) valueDeser;
-        _valueTypeDeserializer = valueTypeDeser;
+        _valueTypeDeserializer = vtd;
+        _valueInstantiator = valueInst;
     }
 
-    public EnumMapDeserializer withResolved(KeyDeserializer keyDeserializer, JsonDeserializer<?> valueDeserializer, TypeDeserializer valueTypeDeser)
+    /**
+     * @since 2.9
+     */
+    protected EnumMapDeserializer(EnumMapDeserializer base,
+            KeyDeserializer keyDeser, JsonDeserializer<?> valueDeser, TypeDeserializer vtd,
+            NullValueProvider nuller)
     {
-        if ((keyDeserializer == _keyDeserializer) && (valueDeserializer == _valueDeserializer) && (valueTypeDeser == _valueTypeDeserializer)) {
-            return this;
-        }
-        return new EnumMapDeserializer(_mapType, keyDeserializer, valueDeserializer, _valueTypeDeserializer);
+        super(base, nuller, base._unwrapSingle);
+        _enumClass = base._enumClass;
+        _keyDeserializer = keyDeser;
+        _valueDeserializer = (JsonDeserializer<Object>) valueDeser;
+        _valueTypeDeserializer = vtd;
+
+        _valueInstantiator = base._valueInstantiator;
+        _delegateDeserializer = base._delegateDeserializer;
+        _propertyBasedCreator = base._propertyBasedCreator;
+    }
+
+    @Deprecated // since 2.9
+    public EnumMapDeserializer(JavaType mapType, KeyDeserializer keyDeser,
+            JsonDeserializer<?> valueDeser, TypeDeserializer vtd)
+    {
+        this(mapType, null, keyDeser, valueDeser, vtd, null);
     }
     
+    public EnumMapDeserializer withResolved(KeyDeserializer keyDeserializer,
+            JsonDeserializer<?> valueDeserializer, TypeDeserializer valueTypeDeser,
+            NullValueProvider nuller)
+    {
+        if ((keyDeserializer == _keyDeserializer) && (nuller == _nullProvider)
+                && (valueDeserializer == _valueDeserializer) && (valueTypeDeser == _valueTypeDeserializer)) {
+            return this;
+        }
+        return new EnumMapDeserializer(this,
+                keyDeserializer, valueDeserializer, valueTypeDeser, nuller);
+    }
+
+    /*
+    /**********************************************************
+    /* Validation, post-processing (ResolvableDeserializer)
+    /**********************************************************
+     */
+    
+    @Override
+    public void resolve(DeserializationContext ctxt) throws JsonMappingException
+    {
+        // May need to resolve types for delegate- and/or property-based creators:
+        if (_valueInstantiator != null) {
+            if (_valueInstantiator.canCreateUsingDelegate()) {
+                JavaType delegateType = _valueInstantiator.getDelegateType(ctxt.getConfig());
+                if (delegateType == null) {
+                    ctxt.reportBadDefinition(_containerType, String.format(
+"Invalid delegate-creator definition for %s: value instantiator (%s) returned true for 'canCreateUsingDelegate()', but null for 'getDelegateType()'",
+                            _containerType,
+                            _valueInstantiator.getClass().getName()));
+                }
+                /* Theoretically should be able to get CreatorProperty for delegate
+                 * parameter to pass; but things get tricky because DelegateCreator
+                 * may contain injectable values. So, for now, let's pass nothing.
+                 */
+                _delegateDeserializer = findDeserializer(ctxt, delegateType, null);
+            } else if (_valueInstantiator.canCreateUsingArrayDelegate()) {
+                JavaType delegateType = _valueInstantiator.getArrayDelegateType(ctxt.getConfig());
+                if (delegateType == null) {
+                    ctxt.reportBadDefinition(_containerType, String.format(
+"Invalid delegate-creator definition for %s: value instantiator (%s) returned true for 'canCreateUsingArrayDelegate()', but null for 'getArrayDelegateType()'",
+                            _containerType,
+                            _valueInstantiator.getClass().getName()));
+                }
+                _delegateDeserializer = findDeserializer(ctxt, delegateType, null);
+            } else if (_valueInstantiator.canCreateFromObjectWith()) {
+                SettableBeanProperty[] creatorProps = _valueInstantiator.getFromObjectArguments(ctxt.getConfig());
+                _propertyBasedCreator = PropertyBasedCreator.construct(ctxt, _valueInstantiator, creatorProps,
+                        ctxt.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES));
+            }
+        }
+    }
+
     /**
      * Method called to finalize setup of this deserializer,
      * when it is known for which property deserializer is needed for.
@@ -69,24 +170,24 @@
         // note: instead of finding key deserializer, with enums we actually
         // work with regular deserializers (less code duplication; but not
         // quite as clean as it ought to be)
-        KeyDeserializer kd = _keyDeserializer;
-        if (kd == null) {
-            kd = ctxt.findKeyDeserializer(_mapType.getKeyType(), property);
+        KeyDeserializer keyDeser = _keyDeserializer;
+        if (keyDeser == null) {
+            keyDeser = ctxt.findKeyDeserializer(_containerType.getKeyType(), property);
         }
-        JsonDeserializer<?> vd = _valueDeserializer;
-        final JavaType vt = _mapType.getContentType();
-        if (vd == null) {
-            vd = ctxt.findContextualValueDeserializer(vt, property);
+        JsonDeserializer<?> valueDeser = _valueDeserializer;
+        final JavaType vt = _containerType.getContentType();
+        if (valueDeser == null) {
+            valueDeser = ctxt.findContextualValueDeserializer(vt, property);
         } else { // if directly assigned, probably not yet contextual, so:
-            vd = ctxt.handleSecondaryContextualization(vd, property, vt);
+            valueDeser = ctxt.handleSecondaryContextualization(valueDeser, property, vt);
         }
         TypeDeserializer vtd = _valueTypeDeserializer;
         if (vtd != null) {
             vtd = vtd.forProperty(property);
         }
-        return withResolved(kd, vd, vtd);
+        return withResolved(keyDeser, valueDeser, vtd, findContentNullProvider(ctxt, property, valueDeser));
     }
-    
+
     /**
      * Because of costs associated with constructing Enum resolvers,
      * let's cache instances by default.
@@ -106,15 +207,16 @@
      */
 
     @Override
-    public JavaType getContentType() {
-        return _mapType.getContentType();
-    }
-
-    @Override
     public JsonDeserializer<Object> getContentDeserializer() {
         return _valueDeserializer;
     }
 
+    // Must override since we do not expose ValueInstantiator
+    @Override // since 2.9
+    public Object getEmptyValue(DeserializationContext ctxt) throws JsonMappingException {
+        return constructMap(ctxt);
+    }
+
     /*
     /**********************************************************
     /* Actual deserialization
@@ -125,49 +227,85 @@
     public EnumMap<?,?> deserialize(JsonParser p, DeserializationContext ctxt)
         throws IOException
     {
+        if (_propertyBasedCreator != null) {
+            return _deserializeUsingProperties(p, ctxt);
+        }
+        if (_delegateDeserializer != null) {
+            return (EnumMap<?,?>) _valueInstantiator.createUsingDelegate(ctxt,
+                    _delegateDeserializer.deserialize(p, ctxt));
+        }
         // Ok: must point to START_OBJECT
-        if (p.getCurrentToken() != JsonToken.START_OBJECT) {
+        JsonToken t = p.getCurrentToken();
+        if ((t != JsonToken.START_OBJECT) && (t != JsonToken.FIELD_NAME) && (t != JsonToken.END_OBJECT)) {
+            // (empty) String may be ok however; or single-String-arg ctor
+            if (t == JsonToken.VALUE_STRING) {
+                return (EnumMap<?,?>) _valueInstantiator.createFromString(ctxt, p.getText());
+            }
+            // slightly redundant (since String was passed above), but also handles empty array case:
             return _deserializeFromEmpty(p, ctxt);
         }
-        EnumMap result = constructMap();
+        EnumMap result = constructMap(ctxt);
+        return deserialize(p, ctxt, result);
+    }
+
+    @Override
+    public EnumMap<?,?> deserialize(JsonParser p, DeserializationContext ctxt,
+            EnumMap result)
+        throws IOException
+    {
+        // [databind#631]: Assign current value, to be accessible by custom deserializers
+        p.setCurrentValue(result);
+
         final JsonDeserializer<Object> valueDes = _valueDeserializer;
         final TypeDeserializer typeDeser = _valueTypeDeserializer;
 
-        while ((p.nextToken()) == JsonToken.FIELD_NAME) {
-            String keyName = p.getCurrentName(); // just for error message
+        String keyStr;
+        if (p.isExpectedStartObjectToken()) {
+            keyStr = p.nextFieldName();
+        } else {
+            JsonToken t = p.getCurrentToken();
+            if (t != JsonToken.FIELD_NAME) {
+                if (t == JsonToken.END_OBJECT) {
+                    return result;
+                }
+                ctxt.reportWrongTokenException(this, JsonToken.FIELD_NAME, null);
+            }
+            keyStr = p.getCurrentName();
+        }
+
+        for (; keyStr != null; keyStr = p.nextFieldName()) {
             // but we need to let key deserializer handle it separately, nonetheless
-            Enum<?> key = (Enum<?>) _keyDeserializer.deserializeKey(keyName, ctxt);
+            Enum<?> key = (Enum<?>) _keyDeserializer.deserializeKey(keyStr, ctxt);
+            JsonToken t = p.nextToken();
             if (key == null) {
                 if (!ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
-                    return (EnumMap<?,?>) ctxt.handleWeirdStringValue(_enumClass, keyName,
+                    return (EnumMap<?,?>) ctxt.handleWeirdStringValue(_enumClass, keyStr,
                             "value not one of declared Enum instance names for %s",
-                            _mapType.getKeyType());
+                            _containerType.getKeyType());
                 }
-                /* 24-Mar-2012, tatu: Null won't work as a key anyway, so let's
-                 *  just skip the entry then. But we must skip the value as well, if so.
-                 */
-                p.nextToken();
+                // 24-Mar-2012, tatu: Null won't work as a key anyway, so let's
+                //  just skip the entry then. But we must skip the value as well, if so.
                 p.skipChildren();
                 continue;
             }
             // And then the value...
-            JsonToken t = p.nextToken();
-            /* note: MUST check for nulls separately: deserializers will
-             * not handle them (and maybe fail or return bogus data)
-             */
+            // note: MUST check for nulls separately: deserializers will
+            // not handle them (and maybe fail or return bogus data)
             Object value;
 
             try {
                 if (t == JsonToken.VALUE_NULL) {
-                    value = valueDes.getNullValue(ctxt);
+                    if (_skipNullValues) {
+                        continue;
+                    }
+                    value = _nullProvider.getNullValue(ctxt);
                 } else if (typeDeser == null) {
                     value =  valueDes.deserialize(p, ctxt);
                 } else {
                     value = valueDes.deserializeWithType(p, ctxt, typeDeser);
                 }
             } catch (Exception e) {
-                wrapAndThrow(e, result, keyName);
-                return null;
+                return wrapAndThrow(e, result, keyStr);
             }
             result.put(key, value);
         }
@@ -175,15 +313,104 @@
     }
 
     @Override
-    public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt, TypeDeserializer typeDeserializer)
-        throws IOException, JsonProcessingException
+    public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
+            TypeDeserializer typeDeserializer)
+        throws IOException
     {
         // In future could check current token... for now this should be enough:
-        return typeDeserializer.deserializeTypedFromObject(jp, ctxt);
+        return typeDeserializer.deserializeTypedFromObject(p, ctxt);
     }
-    
-    protected EnumMap<?,?> constructMap() {
-        return new EnumMap(_enumClass);
+
+    protected EnumMap<?,?> constructMap(DeserializationContext ctxt) throws JsonMappingException {
+        if (_valueInstantiator == null) {
+            return new EnumMap(_enumClass);
+        }
+        try {
+            if (!_valueInstantiator.canCreateUsingDefault()) {
+                return (EnumMap<?,?>) ctxt.handleMissingInstantiator(handledType(),
+                        getValueInstantiator(), null,
+                        "no default constructor found");
+            }
+            return (EnumMap<?,?>) _valueInstantiator.createUsingDefault(ctxt);
+        } catch (IOException e) {
+            return ClassUtil.throwAsMappingException(ctxt, e);
+        }
+    }
+
+    public EnumMap<?,?> _deserializeUsingProperties(JsonParser p, DeserializationContext ctxt) throws IOException
+    {
+        final PropertyBasedCreator creator = _propertyBasedCreator;
+        // null -> no ObjectIdReader for EnumMaps
+        PropertyValueBuffer buffer = creator.startBuilding(p, ctxt, null);
+
+        String keyName;
+        if (p.isExpectedStartObjectToken()) {
+            keyName = p.nextFieldName();
+        } else if (p.hasToken(JsonToken.FIELD_NAME)) {
+            keyName = p.getCurrentName();
+        } else {
+            keyName = null;
+        }
+
+        for (; keyName != null; keyName = p.nextFieldName()) {
+            JsonToken t = p.nextToken(); // to get to value
+            // creator property?
+            SettableBeanProperty prop = creator.findCreatorProperty(keyName);
+            if (prop != null) {
+                // Last property to set?
+                if (buffer.assignParameter(prop, prop.deserialize(p, ctxt))) {
+                    p.nextToken(); // from value to END_OBJECT or FIELD_NAME
+                    EnumMap<?,?> result;
+                    try {
+                        result = (EnumMap<?,?>)creator.build(ctxt, buffer);
+                    } catch (Exception e) {
+                        return wrapAndThrow(e, _containerType.getRawClass(), keyName);
+                    }
+                    return deserialize(p, ctxt, result);
+                }
+                continue;
+            }
+            // other property? needs buffering
+            // but we need to let key deserializer handle it separately, nonetheless
+            Enum<?> key = (Enum<?>) _keyDeserializer.deserializeKey(keyName, ctxt);
+            if (key == null) {
+                if (!ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
+                    return (EnumMap<?,?>) ctxt.handleWeirdStringValue(_enumClass, keyName,
+                            "value not one of declared Enum instance names for %s",
+                            _containerType.getKeyType());
+                }
+                // 24-Mar-2012, tatu: Null won't work as a key anyway, so let's
+                //  just skip the entry then. But we must skip the value as well, if so.
+                p.nextToken();
+                p.skipChildren();
+                continue;
+            }
+            Object value; 
+
+            try {
+                if (t == JsonToken.VALUE_NULL) {
+                    if (_skipNullValues) {
+                        continue;
+                    }
+                    value = _nullProvider.getNullValue(ctxt);
+                } else if (_valueTypeDeserializer == null) {
+                    value = _valueDeserializer.deserialize(p, ctxt);
+                } else {
+                    value = _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer);
+                }
+            } catch (Exception e) {
+                wrapAndThrow(e, _containerType.getRawClass(), keyName);
+                return null;
+            }
+            buffer.bufferMapProperty(key, value);
+        }
+        // end of JSON object?
+        // if so, can just construct and leave...
+        try {
+            return (EnumMap<?,?>)creator.build(ctxt, buffer);
+        } catch (Exception e) {
+            wrapAndThrow(e, _containerType.getRawClass(), keyName);
+            return null;
+        }
     }
 }
-
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumSetDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumSetDeserializer.java
index 2136210..08ceee8 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumSetDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumSetDeserializer.java
@@ -63,7 +63,7 @@
     @SuppressWarnings("unchecked" )
     protected EnumSetDeserializer(EnumSetDeserializer base,
             JsonDeserializer<?> deser, Boolean unwrapSingle) {
-        super(EnumSet.class);
+        super(base);
         _enumType = base._enumType;
         _enumClass = base._enumClass;
         _enumDeserializer = (JsonDeserializer<Enum<?>>) deser;
@@ -96,7 +96,12 @@
         }
         return true;
     }
-    
+
+    @Override // since 2.9
+    public Boolean supportsUpdate(DeserializationConfig config) {
+        return Boolean.TRUE;
+    }
+
     @Override
     public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
             BeanProperty property) throws JsonMappingException
@@ -118,15 +123,32 @@
     /**********************************************************
      */
 
-    @SuppressWarnings("unchecked") 
     @Override
     public EnumSet<?> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
     {
+        EnumSet result = constructSet();
         // Ok: must point to START_ARRAY (or equivalent)
         if (!p.isExpectedStartArrayToken()) {
-            return handleNonArray(p, ctxt);
+            return handleNonArray(p, ctxt, result);
         }
-        EnumSet result = constructSet();
+        return _deserialize(p, ctxt, result);
+    }
+
+    @Override
+    public EnumSet<?> deserialize(JsonParser p, DeserializationContext ctxt,
+            EnumSet<?> result) throws IOException
+    {
+        // Ok: must point to START_ARRAY (or equivalent)
+        if (!p.isExpectedStartArrayToken()) {
+            return handleNonArray(p, ctxt, result);
+        }
+        return _deserialize(p, ctxt, result);
+    }
+    
+    @SuppressWarnings("unchecked") 
+    protected final EnumSet<?> _deserialize(JsonParser p, DeserializationContext ctxt,
+            EnumSet result) throws IOException
+    {
         JsonToken t;
 
         try {
@@ -164,12 +186,12 @@
     @SuppressWarnings("unchecked") 
     private EnumSet constructSet()
     {
-        // superbly ugly... but apparently necessary
         return EnumSet.noneOf(_enumClass);
     }
 
     @SuppressWarnings("unchecked") 
-    protected EnumSet<?> handleNonArray(JsonParser p, DeserializationContext ctxt)
+    protected EnumSet<?> handleNonArray(JsonParser p, DeserializationContext ctxt,
+            EnumSet result)
         throws IOException
     {
         boolean canWrap = (_unwrapSingle == Boolean.TRUE) ||
@@ -179,8 +201,6 @@
         if (!canWrap) {
             return (EnumSet<?>) ctxt.handleUnexpectedToken(EnumSet.class, p);
         }
-
-        EnumSet result = constructSet();
         // First: since `null`s not allowed, slightly simpler...
         if (p.hasToken(JsonToken.VALUE_NULL)) {
             return (EnumSet<?>) ctxt.handleUnexpectedToken(_enumClass, p);
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/FactoryBasedEnumDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/FactoryBasedEnumDeserializer.java
index a6839f2..86a68d9 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/FactoryBasedEnumDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/FactoryBasedEnumDeserializer.java
@@ -1,17 +1,12 @@
 package com.fasterxml.jackson.databind.deser.std;
 
 import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
 
 import com.fasterxml.jackson.core.JsonParser;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.core.JsonToken;
-import com.fasterxml.jackson.databind.BeanProperty;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.DeserializationFeature;
-import com.fasterxml.jackson.databind.JavaType;
-import com.fasterxml.jackson.databind.JsonDeserializer;
-import com.fasterxml.jackson.databind.JsonMappingException;
+
+import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
 import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
 import com.fasterxml.jackson.databind.deser.ValueInstantiator;
@@ -99,6 +94,15 @@
         return this;
     }
 
+    @Override // since 2.9
+    public Boolean supportsUpdate(DeserializationConfig config) {
+        return Boolean.FALSE;
+    }
+
+    // since 2.9.7: should have been the case earlier but
+    @Override
+    public boolean isCachable() { return true; }
+
     @Override
     public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
     {
@@ -113,7 +117,8 @@
                 value = p.getText();
             } else if ((_creatorProps != null) && p.isExpectedStartObjectToken()) {
                 if (_propCreator == null) {
-                    _propCreator = PropertyBasedCreator.construct(ctxt, _valueInstantiator, _creatorProps);
+                    _propCreator = PropertyBasedCreator.construct(ctxt, _valueInstantiator, _creatorProps,
+                            ctxt.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES));
                 }
                 p.nextToken();
                 return deserializeEnumUsingPropertyBased(p, ctxt, _propCreator);
@@ -133,9 +138,9 @@
             return _factory.callOnWith(_valueClass, value);
         } catch (Exception e) {
             Throwable t = ClassUtil.throwRootCauseIfIOE(e);
-            // [databind#1642]
-            if (ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)
-                    && (t instanceof IllegalArgumentException)) {
+            // [databind#1642]:
+            if (ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL) &&
+                    t instanceof IllegalArgumentException) {
                 return null;
             }
             return ctxt.handleInstantiationProblem(_valueClass, value, t);
@@ -176,43 +181,35 @@
     // ************ Got the below methods from BeanDeserializer ********************//
 
     protected final Object _deserializeWithErrorWrapping(JsonParser p, DeserializationContext ctxt,
-    		SettableBeanProperty prop) throws IOException {
-    	try {
-    		return prop.deserialize(p, ctxt);
-    	} catch (Exception e) {
-    		wrapAndThrow(e, _valueClass.getClass(), prop.getName(), ctxt);
-    		// never gets here, unless caller declines to throw an exception
-    		return null;
-    	}
+            SettableBeanProperty prop) throws IOException
+    {
+        try {
+            return prop.deserialize(p, ctxt);
+        } catch (Exception e) {
+            return wrapAndThrow(e, handledType(), prop.getName(), ctxt);
+        }
     }
 
-    public void wrapAndThrow(Throwable t, Object bean, String fieldName, DeserializationContext ctxt)
-    		throws IOException
+    protected Object wrapAndThrow(Throwable t, Object bean, String fieldName, DeserializationContext ctxt)
+            throws IOException
     {
         throw JsonMappingException.wrapWithPath(throwOrReturnThrowable(t, ctxt), bean, fieldName);
     }
 
     private Throwable throwOrReturnThrowable(Throwable t, DeserializationContext ctxt) throws IOException
     {
-        while (t instanceof InvocationTargetException && t.getCause() != null) {
-            t = t.getCause();
-        }
+        t = ClassUtil.getRootCause(t);
         // Errors to be passed as is
-        if (t instanceof Error) {
-            throw (Error) t;
-        }
+        ClassUtil.throwIfError(t);
         boolean wrap = (ctxt == null) || ctxt.isEnabled(DeserializationFeature.WRAP_EXCEPTIONS);
-    	    // Ditto for IOExceptions; except we may want to wrap JSON
-    	    // exceptions
-    	    if (t instanceof IOException) {
-    	        if (!wrap || !(t instanceof JsonProcessingException)) {
-    	            throw (IOException) t;
-    	        }
-    	    } else if (!wrap) {
-    	        if (t instanceof RuntimeException) {
-    	            throw (RuntimeException) t;
-    	        }
-    	    }
-    	    return t;
+        // Ditto for IOExceptions; except we may want to wrap JSON exceptions
+        if (t instanceof IOException) {
+            if (!wrap || !(t instanceof JsonProcessingException)) {
+                throw (IOException) t;
+            }
+        } else if (!wrap) {
+            ClassUtil.throwIfRTE(t);
+        }
+        return t;
     }
-}
\ No newline at end of file
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/FromStringDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/FromStringDeserializer.java
index 0d01e78..68187c1 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/FromStringDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/FromStringDeserializer.java
@@ -14,15 +14,41 @@
 
 import com.fasterxml.jackson.core.*;
 import com.fasterxml.jackson.core.util.VersionUtil;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.JavaType;
-import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.exc.InvalidFormatException;
 import com.fasterxml.jackson.databind.util.ClassUtil;
 
 /**
- * Base class for simple deserializers that only accept JSON String
- * values as the source.
+ * Base class for simple deserializers that serialize values from String
+ * representation: this includes JSON Strings and other Scalar values that
+ * can be coerced into text, like Numbers and Booleans).
+ * Simple JSON String values are trimmed using {@link java.lang.String#trim}.
+ * Partial deserializer implementation will try to first access current token as
+ * a String, calls {@link #_deserialize(String,DeserializationContext)} and
+ * returns return value.
+ * If this does not work (current token not a simple scalar type), attempts
+ * are made so that:
+ *<ul>
+ * <li>Embedded values ({@link JsonToken#VALUE_EMBEDDED_OBJECT}) are returned as-is
+ *    if they are of compatible type
+ *  </li>
+ * <li>Arrays may be "unwrapped" if (and only if) {@link DeserializationFeature#UNWRAP_SINGLE_VALUE_ARRAYS}
+ *    is enabled, and array contains just a single scalar value that can be deserialized
+ *    (for example, JSON Array with single JSON String element).
+ *  </li>
+ * </ul>
+ *<p>
+ * Special handling includes:
+ * <ul>
+ * <li>Null values ({@link JsonToken#VALUE_NULL}) are handled by returning value
+ *   returned by {@link JsonDeserializer#getNullValue(DeserializationContext)}: default
+ *   implementation simply returns Java `null` but this may be overridden.
+ *  </li>
+ * <li>Empty String (after trimming) will result in {@link #_deserializeFromEmptyString}
+ *   getting called, and return value being returned as deserialization: default implementation
+ *   simply returns `null`.
+ *  </li>
+ * </ul>
  */
 @SuppressWarnings("serial")
 public abstract class FromStringDeserializer<T> extends StdScalarDeserializer<T>
@@ -99,16 +125,16 @@
     /* Deserializer implementations
     /**********************************************************
      */
-    
+
     @SuppressWarnings("unchecked")
     @Override
     public T deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
     {
-        // 22-Sep-2012, tatu: For 2.1, use this new method, may force coercion:
+        // Let's get textual value, possibly via coercion from other scalar types
         String text = p.getValueAsString();
         if (text != null) { // has String representation
             if (text.length() == 0 || (text = text.trim()).length() == 0) {
-                // 04-Feb-2013, tatu: Usually should become null; but not always
+                // Usually should become null; but not always
                 return _deserializeFromEmptyString();
             }
             Exception cause = null;
@@ -117,23 +143,18 @@
                 //    indicated error; but that seems wrong. Should be able to return
                 //    `null` as value.
                 return _deserialize(text, ctxt);
-            } catch (IllegalArgumentException iae) {
-                cause = iae;
-            } catch (MalformedURLException me) {
-                cause = me;
+            } catch (IllegalArgumentException | MalformedURLException e) {
+                cause = e;
             }
+            // note: `cause` can't be null
             String msg = "not a valid textual representation";
-            if (cause != null) {
-                String m2 = cause.getMessage();
-                if (m2 != null) {
-                    msg = msg + ", problem: "+m2;
-                }
+            String m2 = cause.getMessage();
+            if (m2 != null) {
+                msg = msg + ", problem: "+m2;
             }
             // 05-May-2016, tatu: Unlike most usage, this seems legit, so...
             JsonMappingException e = ctxt.weirdStringException(text, _valueClass, msg);
-            if (cause != null) {
-                e.initCause(cause);
-            }
+            e.initCause(cause);
             throw e;
             // nothing to do here, yet? We'll fail anyway
         }
@@ -160,7 +181,8 @@
 
     protected T _deserializeEmbedded(Object ob, DeserializationContext ctxt) throws IOException {
         // default impl: error out
-        ctxt.reportMappingException("Don't know how to convert embedded Object of type %s into %s",
+        ctxt.reportInputMismatch(this,
+                "Don't know how to convert embedded Object of type %s into %s",
                 ob.getClass().getName(), _valueClass.getName());
         return null;
     }
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/JsonNodeDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/JsonNodeDeserializer.java
index 05b0562..01937fe 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/JsonNodeDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/JsonNodeDeserializer.java
@@ -22,7 +22,12 @@
      */
     private final static JsonNodeDeserializer instance = new JsonNodeDeserializer();
 
-    protected JsonNodeDeserializer() { super(JsonNode.class); }
+    protected JsonNodeDeserializer() {
+        // `null` means that explicit "merge" is honored and may or may not work, but
+        // that per-type and global defaults do not enable merging. This because
+        // some node types (Object, Array) do support, others don't.
+        super(JsonNode.class, null);
+    }
 
     /**
      * Factory method for accessing deserializer for specific node type
@@ -50,12 +55,6 @@
         return NullNode.getInstance();
     }
 
-    @Override
-    @Deprecated // since 2.6, remove from 2.7
-    public JsonNode getNullValue() {
-        return NullNode.getInstance();
-    }
-
     /**
      * Implementation that will produce types of any JSON nodes; not just one
      * deserializer is registered to handle (in case of more specialized handler).
@@ -70,8 +69,8 @@
         case JsonTokenId.ID_START_ARRAY:
             return deserializeArray(p, ctxt, ctxt.getNodeFactory());
         default:
-            return deserializeAny(p, ctxt, ctxt.getNodeFactory());
         }
+        return deserializeAny(p, ctxt, ctxt.getNodeFactory());
     }
 
     /*
@@ -87,16 +86,19 @@
 
         protected final static ObjectDeserializer _instance = new ObjectDeserializer();
 
-        protected ObjectDeserializer() { super(ObjectNode.class); }
+        protected ObjectDeserializer() { super(ObjectNode.class, true); }
 
         public static ObjectDeserializer getInstance() { return _instance; }
-        
+
         @Override
         public ObjectNode deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
         {
-            if (p.isExpectedStartObjectToken() || p.hasToken(JsonToken.FIELD_NAME)) {
+            if (p.isExpectedStartObjectToken()) {
                 return deserializeObject(p, ctxt, ctxt.getNodeFactory());
             }
+            if (p.hasToken(JsonToken.FIELD_NAME)) {
+                return deserializeObjectAtName(p, ctxt, ctxt.getNodeFactory());
+            }
             // 23-Sep-2015, tatu: Ugh. We may also be given END_OBJECT (similar to FIELD_NAME),
             //    if caller has advanced to the first token of Object, but for empty Object
             if (p.hasToken(JsonToken.END_OBJECT)) {
@@ -104,8 +106,23 @@
             }
             return (ObjectNode) ctxt.handleUnexpectedToken(ObjectNode.class, p);
          }
+
+        /**
+         * Variant needed to support both root-level `updateValue()` and merging.
+         *
+         * @since 2.9
+         */
+        @Override
+        public ObjectNode deserialize(JsonParser p, DeserializationContext ctxt,
+                ObjectNode node) throws IOException
+        {
+            if (p.isExpectedStartObjectToken() || p.hasToken(JsonToken.FIELD_NAME)) {
+                return (ObjectNode) updateObject(p, ctxt, (ObjectNode) node);
+            }
+            return (ObjectNode) ctxt.handleUnexpectedToken(ObjectNode.class, p);
+        }
     }
-        
+
     final static class ArrayDeserializer
         extends BaseNodeDeserializer<ArrayNode>
     {
@@ -113,10 +130,10 @@
 
         protected final static ArrayDeserializer _instance = new ArrayDeserializer();
 
-        protected ArrayDeserializer() { super(ArrayNode.class); }
+        protected ArrayDeserializer() { super(ArrayNode.class, true); }
 
         public static ArrayDeserializer getInstance() { return _instance; }
-        
+
         @Override
         public ArrayNode deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
         {
@@ -125,6 +142,21 @@
             }
             return (ArrayNode) ctxt.handleUnexpectedToken(ArrayNode.class, p);
         }
+
+        /**
+         * Variant needed to support both root-level `updateValue()` and merging.
+         *
+         * @since 2.9
+         */
+        @Override
+        public ArrayNode deserialize(JsonParser p, DeserializationContext ctxt,
+                ArrayNode node) throws IOException
+        {
+            if (p.isExpectedStartArrayToken()) {
+                return (ArrayNode) updateArray(p, ctxt, (ArrayNode) node);
+            }
+            return (ArrayNode) ctxt.handleUnexpectedToken(ArrayNode.class, p);
+        }
     }
 }
 
@@ -136,8 +168,11 @@
 abstract class BaseNodeDeserializer<T extends JsonNode>
     extends StdDeserializer<T>
 {
-    public BaseNodeDeserializer(Class<T> vc) {
+    protected final Boolean _supportsUpdates;
+
+    public BaseNodeDeserializer(Class<T> vc, Boolean supportsUpdates) {
         super(vc);
+        _supportsUpdates = supportsUpdates;
     }
 
     @Override
@@ -145,9 +180,7 @@
             TypeDeserializer typeDeserializer)
         throws IOException
     {
-        /* Output can be as JSON Object, Array or scalar: no way to know
-         * a priori. So:
-         */
+        // Output can be as JSON Object, Array or scalar: no way to know a priori:
         return typeDeserializer.deserializeTypedFromAny(p, ctxt);
     }
 
@@ -158,17 +191,17 @@
     @Override
     public boolean isCachable() { return true; }
 
+    @Override // since 2.9
+    public Boolean supportsUpdate(DeserializationConfig config) {
+        return _supportsUpdates;
+    }
+
     /*
     /**********************************************************
     /* Overridable methods
     /**********************************************************
      */
 
-    @Deprecated // since 2.8
-    protected void _reportProblem(JsonParser p, String msg) throws JsonMappingException {
-        throw JsonMappingException.from(p, msg);
-    }
-
     /**
      * Method called when there is a duplicate value for a field.
      * By default we don't care, and the last value is used.
@@ -190,7 +223,8 @@
     {
         // [databind#237]: Report an error if asked to do so:
         if (ctxt.isEnabled(DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY)) {
-            ctxt.reportMappingException("Duplicate field '%s' for ObjectNode: not allowed when FAIL_ON_READING_DUP_TREE_KEY enabled",
+            ctxt.reportInputMismatch(JsonNode.class,
+                    "Duplicate field '%s' for ObjectNode: not allowed when FAIL_ON_READING_DUP_TREE_KEY enabled",
                     fieldName);
         }
     }
@@ -201,28 +235,20 @@
     /**********************************************************
      */
 
+    /**
+     * Method called to deserialize Object node instance when there is no existing
+     * node to modify.
+     */
     protected final ObjectNode deserializeObject(JsonParser p, DeserializationContext ctxt,
             final JsonNodeFactory nodeFactory) throws IOException
     {
-        ObjectNode node = nodeFactory.objectNode();
-        String key;
-        if (p.isExpectedStartObjectToken()) {
-            key = p.nextFieldName();
-        } else {
-            JsonToken t = p.getCurrentToken();
-            if (t == JsonToken.END_OBJECT) {
-                return node;
-            }
-            if (t != JsonToken.FIELD_NAME) {
-                return (ObjectNode) ctxt.handleUnexpectedToken(handledType(), p);
-            }
-            key = p.getCurrentName();
-        }
+        final ObjectNode node = nodeFactory.objectNode();
+        String key = p.nextFieldName();
         for (; key != null; key = p.nextFieldName()) {
             JsonNode value;
             JsonToken t = p.nextToken();
-            if (t == null) {
-                throw ctxt.mappingException("Unexpected end-of-input when binding data into ObjectNode");
+            if (t == null) { // can this ever occur?
+                t = JsonToken.NOT_AVAILABLE; // can this ever occur?
             }
             switch (t.id()) {
             case JsonTokenId.ID_START_OBJECT:
@@ -261,6 +287,142 @@
         return node;
     }
 
+    /**
+     * Alternate deserialization method used when parser already points to first
+     * FIELD_NAME and not START_OBJECT.
+     *
+     * @since 2.9
+     */
+    protected final ObjectNode deserializeObjectAtName(JsonParser p, DeserializationContext ctxt,
+            final JsonNodeFactory nodeFactory) throws IOException
+    {
+        final ObjectNode node = nodeFactory.objectNode();
+        String key = p.getCurrentName();
+        for (; key != null; key = p.nextFieldName()) {
+            JsonNode value;
+            JsonToken t = p.nextToken();
+            if (t == null) { // can this ever occur?
+                t = JsonToken.NOT_AVAILABLE; // can this ever occur?
+            }
+            switch (t.id()) {
+            case JsonTokenId.ID_START_OBJECT:
+                value = deserializeObject(p, ctxt, nodeFactory);
+                break;
+            case JsonTokenId.ID_START_ARRAY:
+                value = deserializeArray(p, ctxt, nodeFactory);
+                break;
+            case JsonTokenId.ID_EMBEDDED_OBJECT:
+                value = _fromEmbedded(p, ctxt, nodeFactory);
+                break;
+            case JsonTokenId.ID_STRING:
+                value = nodeFactory.textNode(p.getText());
+                break;
+            case JsonTokenId.ID_NUMBER_INT:
+                value = _fromInt(p, ctxt, nodeFactory);
+                break;
+            case JsonTokenId.ID_TRUE:
+                value = nodeFactory.booleanNode(true);
+                break;
+            case JsonTokenId.ID_FALSE:
+                value = nodeFactory.booleanNode(false);
+                break;
+            case JsonTokenId.ID_NULL:
+                value = nodeFactory.nullNode();
+                break;
+            default:
+                value = deserializeAny(p, ctxt, nodeFactory);
+            }
+            JsonNode old = node.replace(key, value);
+            if (old != null) {
+                _handleDuplicateField(p, ctxt, nodeFactory,
+                        key, node, old, value);
+            }
+        }
+        return node;
+    }
+    
+    /**
+     * Alternate deserialization method that is to update existing {@link ObjectNode}
+     * if possible.
+     *
+     * @since 2.9
+     */
+    protected final JsonNode updateObject(JsonParser p, DeserializationContext ctxt,
+        final ObjectNode node) throws IOException
+    {
+        String key;
+        if (p.isExpectedStartObjectToken()) {
+            key = p.nextFieldName();
+        } else {
+            if (!p.hasToken(JsonToken.FIELD_NAME)) {
+                return deserialize(p, ctxt);
+            }
+            key = p.getCurrentName();
+        }
+        for (; key != null; key = p.nextFieldName()) {
+            // If not, fall through to regular handling
+            JsonToken t = p.nextToken();
+
+            // First: see if we can merge things:
+            JsonNode old = node.get(key);
+            if (old != null) {
+                if (old instanceof ObjectNode) {
+                    JsonNode newValue = updateObject(p, ctxt, (ObjectNode) old);
+                    if (newValue != old) {
+                        node.set(key, newValue);
+                    }
+                    continue;
+                }
+                if (old instanceof ArrayNode) {
+                    JsonNode newValue = updateArray(p, ctxt, (ArrayNode) old);
+                    if (newValue != old) {
+                        node.set(key, newValue);
+                    }
+                    continue;
+                }
+            }
+            if (t == null) { // can this ever occur?
+                t = JsonToken.NOT_AVAILABLE;
+            }
+            JsonNode value;
+            JsonNodeFactory nodeFactory = ctxt.getNodeFactory();
+            switch (t.id()) {
+            case JsonTokenId.ID_START_OBJECT:
+                value = deserializeObject(p, ctxt, nodeFactory);
+                break;
+            case JsonTokenId.ID_START_ARRAY:
+                value = deserializeArray(p, ctxt, nodeFactory);
+                break;
+            case JsonTokenId.ID_EMBEDDED_OBJECT:
+                value = _fromEmbedded(p, ctxt, nodeFactory);
+                break;
+            case JsonTokenId.ID_STRING:
+                value = nodeFactory.textNode(p.getText());
+                break;
+            case JsonTokenId.ID_NUMBER_INT:
+                value = _fromInt(p, ctxt, nodeFactory);
+                break;
+            case JsonTokenId.ID_TRUE:
+                value = nodeFactory.booleanNode(true);
+                break;
+            case JsonTokenId.ID_FALSE:
+                value = nodeFactory.booleanNode(false);
+                break;
+            case JsonTokenId.ID_NULL:
+                value = nodeFactory.nullNode();
+                break;
+            default:
+                value = deserializeAny(p, ctxt, nodeFactory);
+            }
+            if (old != null) {
+                _handleDuplicateField(p, ctxt, nodeFactory,
+                        key, node, old, value);
+            }
+            node.set(key, value);
+        }
+        return node;
+    }
+
     protected final ArrayNode deserializeArray(JsonParser p, DeserializationContext ctxt,
             final JsonNodeFactory nodeFactory) throws IOException
     {
@@ -301,16 +463,60 @@
         }
     }
 
+    /**
+     * Alternate deserialization method that is to update existing {@link ObjectNode}
+     * if possible.
+     *
+     * @since 2.9
+     */
+    protected final JsonNode updateArray(JsonParser p, DeserializationContext ctxt,
+        final ArrayNode node) throws IOException
+    {
+        final JsonNodeFactory nodeFactory = ctxt.getNodeFactory();
+        while (true) {
+            JsonToken t = p.nextToken();
+            switch (t.id()) {
+            case JsonTokenId.ID_START_OBJECT:
+                node.add(deserializeObject(p, ctxt, nodeFactory));
+                break;
+            case JsonTokenId.ID_START_ARRAY:
+                node.add(deserializeArray(p, ctxt, nodeFactory));
+                break;
+            case JsonTokenId.ID_END_ARRAY:
+                return node;
+            case JsonTokenId.ID_EMBEDDED_OBJECT:
+                node.add(_fromEmbedded(p, ctxt, nodeFactory));
+                break;
+            case JsonTokenId.ID_STRING:
+                node.add(nodeFactory.textNode(p.getText()));
+                break;
+            case JsonTokenId.ID_NUMBER_INT:
+                node.add(_fromInt(p, ctxt, nodeFactory));
+                break;
+            case JsonTokenId.ID_TRUE:
+                node.add(nodeFactory.booleanNode(true));
+                break;
+            case JsonTokenId.ID_FALSE:
+                node.add(nodeFactory.booleanNode(false));
+                break;
+            case JsonTokenId.ID_NULL:
+                node.add(nodeFactory.nullNode());
+                break;
+            default:
+                node.add(deserializeAny(p, ctxt, nodeFactory));
+                break;
+            }
+        }
+    }
+    
     protected final JsonNode deserializeAny(JsonParser p, DeserializationContext ctxt,
             final JsonNodeFactory nodeFactory) throws IOException
     {
         switch (p.getCurrentTokenId()) {
-        case JsonTokenId.ID_START_OBJECT:
-        case JsonTokenId.ID_END_OBJECT: // for empty JSON Objects we may point to this
+        case JsonTokenId.ID_END_OBJECT: // for empty JSON Objects we may point to this?
+            return nodeFactory.objectNode();
         case JsonTokenId.ID_FIELD_NAME:
-            return deserializeObject(p, ctxt, nodeFactory);
-        case JsonTokenId.ID_START_ARRAY:
-            return deserializeArray(p, ctxt, nodeFactory);
+            return deserializeObjectAtName(p, ctxt, nodeFactory);
         case JsonTokenId.ID_EMBEDDED_OBJECT:
             return _fromEmbedded(p, ctxt, nodeFactory);
         case JsonTokenId.ID_STRING:
@@ -325,8 +531,16 @@
             return nodeFactory.booleanNode(false);
         case JsonTokenId.ID_NULL:
             return nodeFactory.nullNode();
+
+            /* Caller checks for these, should not get here ever
+        case JsonTokenId.ID_START_OBJECT:
+            return deserializeObject(p, ctxt, nodeFactory);
+        case JsonTokenId.ID_START_ARRAY:
+            return deserializeArray(p, ctxt, nodeFactory);
+            */
+
             
-            // These states can not be mapped; input stream is
+            // These states cannot be mapped; input stream is
             // off by an event or two
 
         //case END_OBJECT:
@@ -371,12 +585,8 @@
         if (ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) {
             // 20-May-2016, tatu: As per [databind#1028], need to be careful
             //   (note: JDK 1.8 would have `Double.isFinite()`)
-            // 21-Aug-2016, tatu: Not optimal, really, because this may result in
-            //   value getting parsed twice. But has to do for now, to resolve
-            //  [databind#1315]
-            double d = p.getDoubleValue();
-            if (Double.isInfinite(d) || Double.isNaN(d)) {
-                return nodeFactory.numberNode(d);
+            if (p.isNaN()) {
+                return nodeFactory.numberNode(p.getDoubleValue());
             }
             return nodeFactory.numberNode(p.getDecimalValue());
         }
@@ -402,7 +612,7 @@
             return nodeFactory.rawValueNode((RawValue) ob);
         }
         if (ob instanceof JsonNode) {
-            // [Issue#433]: but could also be a JsonNode hiding in there!
+            // [databind#433]: but could also be a JsonNode hiding in there!
             return (JsonNode) ob;
         }
         // any other special handling needed?
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/MapDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/MapDeserializer.java
index c1d8719..5e4bb35 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/MapDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/MapDeserializer.java
@@ -4,7 +4,9 @@
 import java.util.*;
 
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+
 import com.fasterxml.jackson.core.*;
+
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
 import com.fasterxml.jackson.databind.deser.*;
@@ -33,8 +35,6 @@
 
     // // Configuration: typing, deserializers
 
-    protected final JavaType _mapType;
-
     /**
      * Key deserializer to use; either passed via constructor
      * (when indicated by annotations), or resolved when
@@ -61,13 +61,11 @@
      * is the type deserializer that can handle it
      */
     protected final TypeDeserializer _valueTypeDeserializer;
-    
+
     // // Instance construction settings:
 
     protected final ValueInstantiator _valueInstantiator;
 
-    protected final boolean _hasDefaultCreator;
-
     /**
      * Deserializer that is used iff delegate-based creator is
      * to be used for deserializing from JSON Object.
@@ -82,8 +80,10 @@
      */
     protected PropertyBasedCreator _propertyBasedCreator;    
 
+    protected final boolean _hasDefaultCreator;
+
     // // Any properties to ignore if seen?
-    
+
     protected Set<String> _ignorableProperties;
 
     /*
@@ -96,8 +96,7 @@
             KeyDeserializer keyDeser, JsonDeserializer<Object> valueDeser,
             TypeDeserializer valueTypeDeser)
     {
-        super(mapType);
-        _mapType = mapType;
+        super(mapType, null, null);
         _keyDeserializer = keyDeser;
         _valueDeserializer = valueDeser;
         _valueTypeDeserializer = valueTypeDeser;
@@ -114,8 +113,7 @@
      */
     protected MapDeserializer(MapDeserializer src)
     {
-        super(src._mapType);
-        _mapType = src._mapType;
+        super(src);
         _keyDeserializer = src._keyDeserializer;
         _valueDeserializer = src._valueDeserializer;
         _valueTypeDeserializer = src._valueTypeDeserializer;
@@ -132,10 +130,10 @@
     protected MapDeserializer(MapDeserializer src,
             KeyDeserializer keyDeser, JsonDeserializer<Object> valueDeser,
             TypeDeserializer valueTypeDeser,
+            NullValueProvider nuller,
             Set<String> ignorable)
     {
-        super(src._mapType);
-        _mapType = src._mapType;
+        super(src, nuller, src._unwrapSingle);
         _keyDeserializer = keyDeser;
         _valueDeserializer = valueDeser;
         _valueTypeDeserializer = valueTypeDeser;
@@ -145,7 +143,7 @@
         _hasDefaultCreator = src._hasDefaultCreator;
         _ignorableProperties = ignorable;
 
-        _standardStringKey = _isStdKeyDeser(_mapType, keyDeser);
+        _standardStringKey = _isStdKeyDeser(_containerType, keyDeser);
     }
 
     /**
@@ -155,15 +153,18 @@
     @SuppressWarnings("unchecked")
     protected MapDeserializer withResolved(KeyDeserializer keyDeser,
             TypeDeserializer valueTypeDeser, JsonDeserializer<?> valueDeser,
+            NullValueProvider nuller,
             Set<String> ignorable)
     {
         
         if ((_keyDeserializer == keyDeser) && (_valueDeserializer == valueDeser)
-                && (_valueTypeDeserializer == valueTypeDeser) && (_ignorableProperties == ignorable)) {
+                && (_valueTypeDeserializer == valueTypeDeser) && (_nullProvider == nuller)
+                && (_ignorableProperties == ignorable)) {
             return this;
         }
         return new MapDeserializer(this,
-                keyDeser, (JsonDeserializer<Object>) valueDeser, valueTypeDeser, ignorable);
+                keyDeser, (JsonDeserializer<Object>) valueDeser, valueTypeDeser,
+                nuller, ignorable);
     }
 
     /**
@@ -204,34 +205,34 @@
     public void resolve(DeserializationContext ctxt) throws JsonMappingException
     {
         // May need to resolve types for delegate- and/or property-based creators:
-        if (_valueInstantiator != null) {
-            if (_valueInstantiator.canCreateUsingDelegate()) {
-                JavaType delegateType = _valueInstantiator.getDelegateType(ctxt.getConfig());
-                if (delegateType == null) {
-                    throw new IllegalArgumentException("Invalid delegate-creator definition for "+_mapType
-                            +": value instantiator ("+_valueInstantiator.getClass().getName()
-                            +") returned true for 'canCreateUsingDelegate()', but null for 'getDelegateType()'");
-                }
-                /* Theoretically should be able to get CreatorProperty for delegate
-                 * parameter to pass; but things get tricky because DelegateCreator
-                 * may contain injectable values. So, for now, let's pass nothing.
-                 */
-                _delegateDeserializer = findDeserializer(ctxt, delegateType, null);
-            } else if (_valueInstantiator.canCreateUsingArrayDelegate()) {
-                JavaType delegateType = _valueInstantiator.getArrayDelegateType(ctxt.getConfig());
-                if (delegateType == null) {
-                    throw new IllegalArgumentException("Invalid delegate-creator definition for "+_mapType
-                            +": value instantiator ("+_valueInstantiator.getClass().getName()
-                            +") returned true for 'canCreateUsingDelegate()', but null for 'getArrayDelegateType()'");
-                }
-                _delegateDeserializer = findDeserializer(ctxt, delegateType, null);
+        if (_valueInstantiator.canCreateUsingDelegate()) {
+            JavaType delegateType = _valueInstantiator.getDelegateType(ctxt.getConfig());
+            if (delegateType == null) {
+                ctxt.reportBadDefinition(_containerType, String.format(
+"Invalid delegate-creator definition for %s: value instantiator (%s) returned true for 'canCreateUsingDelegate()', but null for 'getDelegateType()'",
+                _containerType,
+                _valueInstantiator.getClass().getName()));
             }
+            // Theoretically should be able to get CreatorProperty for delegate
+            // parameter to pass; but things get tricky because DelegateCreator
+            // may contain injectable values. So, for now, let's pass nothing.
+            _delegateDeserializer = findDeserializer(ctxt, delegateType, null);
+        } else if (_valueInstantiator.canCreateUsingArrayDelegate()) {
+            JavaType delegateType = _valueInstantiator.getArrayDelegateType(ctxt.getConfig());
+            if (delegateType == null) {
+                ctxt.reportBadDefinition(_containerType, String.format(
+"Invalid delegate-creator definition for %s: value instantiator (%s) returned true for 'canCreateUsingArrayDelegate()', but null for 'getArrayDelegateType()'",
+                    _containerType,
+                    _valueInstantiator.getClass().getName()));
+            }
+            _delegateDeserializer = findDeserializer(ctxt, delegateType, null);
         }
         if (_valueInstantiator.canCreateFromObjectWith()) {
             SettableBeanProperty[] creatorProps = _valueInstantiator.getFromObjectArguments(ctxt.getConfig());
-            _propertyBasedCreator = PropertyBasedCreator.construct(ctxt, _valueInstantiator, creatorProps);
+            _propertyBasedCreator = PropertyBasedCreator.construct(ctxt, _valueInstantiator, creatorProps,
+                    ctxt.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES));
         }
-        _standardStringKey = _isStdKeyDeser(_mapType, _keyDeserializer);
+        _standardStringKey = _isStdKeyDeser(_containerType, _keyDeserializer);
     }
 
     /**
@@ -242,25 +243,25 @@
     public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
             BeanProperty property) throws JsonMappingException
     {
-        KeyDeserializer kd = _keyDeserializer;
-        if (kd == null) {
-            kd = ctxt.findKeyDeserializer(_mapType.getKeyType(), property);
+        KeyDeserializer keyDeser = _keyDeserializer;
+        if (keyDeser == null) {
+            keyDeser = ctxt.findKeyDeserializer(_containerType.getKeyType(), property);
         } else {
-            if (kd instanceof ContextualKeyDeserializer) {
-                kd = ((ContextualKeyDeserializer) kd).createContextual(ctxt, property);
+            if (keyDeser instanceof ContextualKeyDeserializer) {
+                keyDeser = ((ContextualKeyDeserializer) keyDeser).createContextual(ctxt, property);
             }
         }
         
-        JsonDeserializer<?> vd = _valueDeserializer;
+        JsonDeserializer<?> valueDeser = _valueDeserializer;
         // [databind#125]: May have a content converter
         if (property != null) {
-            vd = findConvertingContentDeserializer(ctxt, property, vd);
+            valueDeser = findConvertingContentDeserializer(ctxt, property, valueDeser);
         }
-        final JavaType vt = _mapType.getContentType();
-        if (vd == null) {
-            vd = ctxt.findContextualValueDeserializer(vt, property);
+        final JavaType vt = _containerType.getContentType();
+        if (valueDeser == null) {
+            valueDeser = ctxt.findContextualValueDeserializer(vt, property);
         } else { // if directly assigned, probably not yet contextual, so:
-            vd = ctxt.handleSecondaryContextualization(vd, property, vt);
+            valueDeser = ctxt.handleSecondaryContextualization(valueDeser, property, vt);
         }
         TypeDeserializer vtd = _valueTypeDeserializer;
         if (vtd != null) {
@@ -268,7 +269,7 @@
         }
         Set<String> ignored = _ignorableProperties;
         AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
-        if (intr != null && property != null) {
+        if (_neitherNull(intr, property)) {
             AnnotatedMember member = property.getMember();
             if (member != null) {
                 JsonIgnoreProperties.Value ignorals = intr.findPropertyIgnorals(member);
@@ -283,7 +284,8 @@
                 }
             }
         }
-        return withResolved(kd, vtd, vd, ignored);
+        return withResolved(keyDeser, vtd, valueDeser,
+                findContentNullProvider(ctxt, property, valueDeser), ignored);
     }
 
     /*
@@ -293,15 +295,15 @@
      */
 
     @Override
-    public JavaType getContentType() {
-        return _mapType.getContentType();
-    }
-
-    @Override
     public JsonDeserializer<Object> getContentDeserializer() {
         return _valueDeserializer;
     }
-    
+
+    @Override
+    public ValueInstantiator getValueInstantiator() {
+        return _valueInstantiator;
+    }
+
     /*
     /**********************************************************
     /* JsonDeserializer API
@@ -323,9 +325,8 @@
      */
     @Override
     public boolean isCachable() {
-        /* As per [databind#735], existence of value or key deserializer (only passed
-         * if annotated to use non-standard one) should also prevent caching.
-         */
+        // As per [databind#735], existence of value or key deserializer (only passed
+        // if annotated to use non-standard one) should also prevent caching.
         return (_valueDeserializer == null)
                 && (_keyDeserializer == null)
                 && (_valueTypeDeserializer == null)
@@ -344,17 +345,18 @@
                     _delegateDeserializer.deserialize(p, ctxt));
         }
         if (!_hasDefaultCreator) {
-            return (Map<Object,Object> ) ctxt.handleMissingInstantiator(getMapClass(), p,
+            return (Map<Object,Object> ) ctxt.handleMissingInstantiator(getMapClass(),
+                    getValueInstantiator(), p,
                     "no default constructor found");
         }
         // Ok: must point to START_OBJECT, FIELD_NAME or END_OBJECT
         JsonToken t = p.getCurrentToken();
         if (t != JsonToken.START_OBJECT && t != JsonToken.FIELD_NAME && t != JsonToken.END_OBJECT) {
-            // (empty) String may be ok however:
+            // (empty) String may be ok however; or single-String-arg ctor
             if (t == JsonToken.VALUE_STRING) {
                 return (Map<Object,Object>) _valueInstantiator.createFromString(ctxt, p.getText());
             }
-            // slightly redundant (since String was passed above), but
+            // slightly redundant (since String was passed above), but also handles empty array case:
             return _deserializeFromEmpty(p, ctxt);
         }
         final Map<Object,Object> result = (Map<Object,Object>) _valueInstantiator.createUsingDefault(ctxt);
@@ -372,7 +374,7 @@
             Map<Object,Object> result)
         throws IOException
     {
-        // [databind#631]: Assign current value, to be accessible by custom serializers
+        // [databind#631]: Assign current value, to be accessible by custom deserializers
         p.setCurrentValue(result);
         
         // Ok: must point to START_OBJECT or FIELD_NAME
@@ -380,23 +382,24 @@
         if (t != JsonToken.START_OBJECT && t != JsonToken.FIELD_NAME) {
             return (Map<Object,Object>) ctxt.handleUnexpectedToken(getMapClass(), p);
         }
+        // 21-Apr-2017, tatu: Need separate methods to do proper merging
         if (_standardStringKey) {
-            _readAndBindStringKeyMap(p, ctxt, result);
+            _readAndUpdateStringKeyMap(p, ctxt, result);
             return result;
         }
-        _readAndBind(p, ctxt, result);
+        _readAndUpdate(p, ctxt, result);
         return result;
     }
 
     @Override
-    public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt,
+    public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
             TypeDeserializer typeDeserializer)
-        throws IOException, JsonProcessingException
+        throws IOException
     {
         // In future could check current token... for now this should be enough:
-        return typeDeserializer.deserializeTypedFromObject(jp, ctxt);
+        return typeDeserializer.deserializeTypedFromObject(p, ctxt);
     }
-    
+
     /*
     /**********************************************************
     /* Other public accessors
@@ -404,13 +407,13 @@
      */
 
     @SuppressWarnings("unchecked")
-    public final Class<?> getMapClass() { return (Class<Map<Object,Object>>) _mapType.getRawClass(); }
+    public final Class<?> getMapClass() { return (Class<Map<Object,Object>>) _containerType.getRawClass(); }
 
-    @Override public JavaType getValueType() { return _mapType; }
+    @Override public JavaType getValueType() { return _containerType; }
 
     /*
     /**********************************************************
-    /* Internal methods
+    /* Internal methods, non-merging deserialization
     /**********************************************************
      */
 
@@ -424,7 +427,8 @@
         MapReferringAccumulator referringAccumulator = null;
         boolean useObjectId = valueDes.getObjectIdReader() != null;
         if (useObjectId) {
-            referringAccumulator = new MapReferringAccumulator(_mapType.getContentType().getRawClass(), result);
+            referringAccumulator = new MapReferringAccumulator(_containerType.getContentType().getRawClass(),
+                    result);
         }
 
         String keyStr;
@@ -432,11 +436,11 @@
             keyStr = p.nextFieldName();
         } else {
             JsonToken t = p.getCurrentToken();
-            if (t == JsonToken.END_OBJECT) {
-                return;
-            }
             if (t != JsonToken.FIELD_NAME) {
-                ctxt.reportWrongTokenException(p, JsonToken.FIELD_NAME, null);
+                if (t == JsonToken.END_OBJECT) {
+                    return;
+                }
+                ctxt.reportWrongTokenException(this, JsonToken.FIELD_NAME, null);
             }
             keyStr = p.getCurrentName();
         }
@@ -453,7 +457,10 @@
                 // Note: must handle null explicitly here; value deserializers won't
                 Object value;
                 if (t == JsonToken.VALUE_NULL) {
-                    value = valueDes.getNullValue(ctxt);
+                    if (_skipNullValues) {
+                        continue;
+                    }
+                    value = _nullProvider.getNullValue(ctxt);
                 } else if (typeDeser == null) {
                     value = valueDes.deserialize(p, ctxt);
                 } else {
@@ -465,7 +472,7 @@
                     result.put(key, value);
                 }
             } catch (UnresolvedForwardReference reference) {
-                handleUnresolvedReference(p, referringAccumulator, key, reference);
+                handleUnresolvedReference(ctxt, referringAccumulator, key, reference);
             } catch (Exception e) {
                 wrapAndThrow(e, result, keyStr);
             }
@@ -485,7 +492,7 @@
         MapReferringAccumulator referringAccumulator = null;
         boolean useObjectId = (valueDes.getObjectIdReader() != null);
         if (useObjectId) {
-            referringAccumulator = new MapReferringAccumulator(_mapType.getContentType().getRawClass(), result);
+            referringAccumulator = new MapReferringAccumulator(_containerType.getContentType().getRawClass(), result);
         }
         
         String key;
@@ -497,7 +504,7 @@
                 return;
             }
             if (t != JsonToken.FIELD_NAME) {
-                ctxt.reportWrongTokenException(p, JsonToken.FIELD_NAME, null);
+                ctxt.reportWrongTokenException(this, JsonToken.FIELD_NAME, null);
             }
             key = p.getCurrentName();
         }
@@ -512,7 +519,10 @@
                 // Note: must handle null explicitly here; value deserializers won't
                 Object value;
                 if (t == JsonToken.VALUE_NULL) {
-                    value = valueDes.getNullValue(ctxt);
+                    if (_skipNullValues) {
+                        continue;
+                    }
+                    value = _nullProvider.getNullValue(ctxt);
                 } else if (typeDeser == null) {
                     value = valueDes.deserialize(p, ctxt);
                 } else {
@@ -524,14 +534,14 @@
                     result.put(key, value);
                 }
             } catch (UnresolvedForwardReference reference) {
-                handleUnresolvedReference(p, referringAccumulator, key, reference);
+                handleUnresolvedReference(ctxt, referringAccumulator, key, reference);
             } catch (Exception e) {
                 wrapAndThrow(e, result, key);
             }
         }
         // 23-Mar-2015, tatu: TODO: verify we got END_OBJECT?
     }
-
+    
     @SuppressWarnings("unchecked") 
     public Map<Object,Object> _deserializeUsingCreator(JsonParser p, DeserializationContext ctxt) throws IOException
     {
@@ -562,13 +572,12 @@
             if (prop != null) {
                 // Last property to set?
                 if (buffer.assignParameter(prop, prop.deserialize(p, ctxt))) {
-                    p.nextToken();
+                    p.nextToken(); // from value to END_OBJECT or FIELD_NAME
                     Map<Object,Object> result;
                     try {
                         result = (Map<Object,Object>)creator.build(ctxt, buffer);
                     } catch (Exception e) {
-                        wrapAndThrow(e, _mapType.getRawClass(), key);
-                        return null;
+                        return wrapAndThrow(e, _containerType.getRawClass(), key);
                     }
                     _readAndBind(p, ctxt, result);
                     return result;
@@ -581,14 +590,17 @@
 
             try {
                 if (t == JsonToken.VALUE_NULL) {
-                    value = valueDes.getNullValue(ctxt);
+                    if (_skipNullValues) {
+                        continue;
+                    }
+                    value = _nullProvider.getNullValue(ctxt);
                 } else if (typeDeser == null) {
                     value = valueDes.deserialize(p, ctxt);
                 } else {
                     value = valueDes.deserializeWithType(p, ctxt, typeDeser);
                 }
             } catch (Exception e) {
-                wrapAndThrow(e, _mapType.getRawClass(), key);
+                wrapAndThrow(e, _containerType.getRawClass(), key);
                 return null;
             }
             buffer.bufferMapProperty(actualKey, value);
@@ -598,22 +610,156 @@
         try {
             return (Map<Object,Object>)creator.build(ctxt, buffer);
         } catch (Exception e) {
-            wrapAndThrow(e, _mapType.getRawClass(), key);
+            wrapAndThrow(e, _containerType.getRawClass(), key);
             return null;
         }
     }
 
-    @Deprecated // since 2.5
-    protected void wrapAndThrow(Throwable t, Object ref) throws IOException {
-        wrapAndThrow(t, ref, null);
+    /*
+    /**********************************************************
+    /* Internal methods, non-merging deserialization
+    /**********************************************************
+     */
+
+    /**
+     * @since 2.9
+     */
+    protected final void _readAndUpdate(JsonParser p, DeserializationContext ctxt,
+            Map<Object,Object> result) throws IOException
+    {
+        final KeyDeserializer keyDes = _keyDeserializer;
+        final JsonDeserializer<Object> valueDes = _valueDeserializer;
+        final TypeDeserializer typeDeser = _valueTypeDeserializer;
+
+        // Note: assumption is that Object Id handling can't really work with merging
+        // and thereby we can (and should) just drop that part
+
+        String keyStr;
+        if (p.isExpectedStartObjectToken()) {
+            keyStr = p.nextFieldName();
+        } else {
+            JsonToken t = p.getCurrentToken();
+            if (t == JsonToken.END_OBJECT) {
+                return;
+            }
+            if (t != JsonToken.FIELD_NAME) {
+                ctxt.reportWrongTokenException(this, JsonToken.FIELD_NAME, null);
+            }
+            keyStr = p.getCurrentName();
+        }
+        
+        for (; keyStr != null; keyStr = p.nextFieldName()) {
+            Object key = keyDes.deserializeKey(keyStr, ctxt);
+            // And then the value...
+            JsonToken t = p.nextToken();
+            if (_ignorableProperties != null && _ignorableProperties.contains(keyStr)) {
+                p.skipChildren();
+                continue;
+            }
+            try {
+                // Note: must handle null explicitly here, can't merge etc
+                if (t == JsonToken.VALUE_NULL) {
+                    if (_skipNullValues) {
+                        continue;
+                    }
+                    result.put(key, _nullProvider.getNullValue(ctxt));
+                    continue;
+                }
+                Object old = result.get(key);
+                Object value;
+                if (old != null) {
+                    value = valueDes.deserialize(p, ctxt, old);
+                } else if (typeDeser == null) {
+                    value = valueDes.deserialize(p, ctxt);
+                } else {
+                    value = valueDes.deserializeWithType(p, ctxt, typeDeser);
+                }
+                if (value != old) {
+                    result.put(key, value);
+                }
+            } catch (Exception e) {
+                wrapAndThrow(e, result, keyStr);
+            }
+        }
     }
 
-    private void handleUnresolvedReference(JsonParser jp, MapReferringAccumulator accumulator,
+    /**
+     * Optimized method used when keys can be deserialized as plain old
+     * {@link java.lang.String}s, and there is no custom deserialized
+     * specified.
+     *
+     * @since 2.9
+     */
+    protected final void _readAndUpdateStringKeyMap(JsonParser p, DeserializationContext ctxt,
+            Map<Object,Object> result) throws IOException
+    {
+        final JsonDeserializer<Object> valueDes = _valueDeserializer;
+        final TypeDeserializer typeDeser = _valueTypeDeserializer;
+
+        // Note: assumption is that Object Id handling can't really work with merging
+        // and thereby we can (and should) just drop that part
+
+        String key;
+        if (p.isExpectedStartObjectToken()) {
+            key = p.nextFieldName();
+        } else {
+            JsonToken t = p.getCurrentToken();
+            if (t == JsonToken.END_OBJECT) {
+                return;
+            }
+            if (t != JsonToken.FIELD_NAME) {
+                ctxt.reportWrongTokenException(this, JsonToken.FIELD_NAME, null);
+            }
+            key = p.getCurrentName();
+        }
+
+        for (; key != null; key = p.nextFieldName()) {
+            JsonToken t = p.nextToken();
+            if (_ignorableProperties != null && _ignorableProperties.contains(key)) {
+                p.skipChildren();
+                continue;
+            }
+            try {
+                // Note: must handle null explicitly here, can't merge etc
+                if (t == JsonToken.VALUE_NULL) {
+                    if (_skipNullValues) {
+                        continue;
+                    }
+                    result.put(key, _nullProvider.getNullValue(ctxt));
+                    continue;
+                }
+                Object old = result.get(key);
+                Object value;
+                if (old != null) {
+                    value = valueDes.deserialize(p, ctxt, old);
+                } else if (typeDeser == null) {
+                    value = valueDes.deserialize(p, ctxt);
+                } else {
+                    value = valueDes.deserializeWithType(p, ctxt, typeDeser);
+                }
+                if (value != old) {
+                    result.put(key, value);
+                }
+            } catch (Exception e) {
+                wrapAndThrow(e, result, key);
+            }
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods, other
+    /**********************************************************
+     */
+
+    private void handleUnresolvedReference(DeserializationContext ctxt,
+            MapReferringAccumulator accumulator,
             Object key, UnresolvedForwardReference reference)
         throws JsonMappingException
     {
         if (accumulator == null) {
-            throw JsonMappingException.from(jp, "Unresolved forward reference but no identity info.", reference);
+            ctxt.reportInputMismatch(this,
+                    "Unresolved forward reference but no identity info: "+reference);
         }
         Referring referring = accumulator.handleUnresolvedReference(reference, key);
         reference.getRoid().appendReferring(referring);
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/MapEntryDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/MapEntryDeserializer.java
index 7899712..2805c6b 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/MapEntryDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/MapEntryDeserializer.java
@@ -27,8 +27,6 @@
 
     // // Configuration: typing, deserializers
 
-    protected final JavaType _type;
-
     /**
      * Key deserializer to use; either passed via constructor
      * (when indicated by annotations), or resolved when
@@ -61,7 +59,6 @@
         if (type.containedTypeCount() != 2) { // sanity check
             throw new IllegalArgumentException("Missing generic type information for "+type);
         }
-        _type = type;
         _keyDeserializer = keyDeser;
         _valueDeserializer = valueDeser;
         _valueTypeDeserializer = valueTypeDeser;
@@ -73,8 +70,7 @@
      */
     protected MapEntryDeserializer(MapEntryDeserializer src)
     {
-        super(src._type);
-        _type = src._type;
+        super(src);
         _keyDeserializer = src._keyDeserializer;
         _valueDeserializer = src._valueDeserializer;
         _valueTypeDeserializer = src._valueTypeDeserializer;
@@ -84,8 +80,7 @@
             KeyDeserializer keyDeser, JsonDeserializer<Object> valueDeser,
             TypeDeserializer valueTypeDeser)
     {
-        super(src._type);
-        _type = src._type;
+        super(src);
         _keyDeserializer = keyDeser;
         _valueDeserializer = valueDeser;
         _valueTypeDeserializer = valueTypeDeser;
@@ -124,7 +119,7 @@
     {
         KeyDeserializer kd = _keyDeserializer;
         if (kd == null) {
-            kd = ctxt.findKeyDeserializer(_type.containedType(0), property);
+            kd = ctxt.findKeyDeserializer(_containerType.containedType(0), property);
         } else {
             if (kd instanceof ContextualKeyDeserializer) {
                 kd = ((ContextualKeyDeserializer) kd).createContextual(ctxt, property);
@@ -132,7 +127,7 @@
         }
         JsonDeserializer<?> vd = _valueDeserializer;
         vd = findConvertingContentDeserializer(ctxt, property, vd);
-        JavaType contentType = _type.containedType(1);
+        JavaType contentType = _containerType.containedType(1);
         if (vd == null) {
             vd = ctxt.findContextualValueDeserializer(contentType, property);
         } else { // if directly assigned, probably not yet contextual, so:
@@ -153,7 +148,7 @@
 
     @Override
     public JavaType getContentType() {
-        return _type.containedType(1);
+        return _containerType.containedType(1);
     }
 
     @Override
@@ -183,8 +178,8 @@
         }
         if (t != JsonToken.FIELD_NAME) {
             if (t == JsonToken.END_OBJECT) {
-                ctxt.reportMappingException("Can not deserialize a Map.Entry out of empty JSON Object");
-                return null;
+                return ctxt.reportInputMismatch(this,
+                        "Cannot deserialize a Map.Entry out of empty JSON Object");
             }
             return (Map.Entry<Object,Object>) ctxt.handleUnexpectedToken(handledType(), p);
         }
@@ -215,10 +210,13 @@
         t = p.nextToken();
         if (t != JsonToken.END_OBJECT) {
             if (t == JsonToken.FIELD_NAME) { // most likely
-                ctxt.reportMappingException("Problem binding JSON into Map.Entry: more than one entry in JSON (second field: '"+p.getCurrentName()+"')");
+                ctxt.reportInputMismatch(this,
+                        "Problem binding JSON into Map.Entry: more than one entry in JSON (second field: '%s')",
+                        p.getCurrentName());
             } else {
                 // how would this occur?
-                ctxt.reportMappingException("Problem binding JSON into Map.Entry: unexpected content after JSON Object entry: "+t);
+                ctxt.reportInputMismatch(this,
+                        "Problem binding JSON into Map.Entry: unexpected content after JSON Object entry: "+t);
             }
             return null;
         }
@@ -229,23 +227,15 @@
     public Map.Entry<Object,Object> deserialize(JsonParser p, DeserializationContext ctxt,
             Map.Entry<Object,Object> result) throws IOException
     {
-        throw new IllegalStateException("Can not update Map.Entry values");
+        throw new IllegalStateException("Cannot update Map.Entry values");
     }
 
     @Override
     public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
             TypeDeserializer typeDeserializer)
-        throws IOException, JsonProcessingException
+        throws IOException
     {
         // In future could check current token... for now this should be enough:
         return typeDeserializer.deserializeTypedFromObject(p, ctxt);
     }
-
-    /*
-    /**********************************************************
-    /* Other public accessors
-    /**********************************************************
-     */
-
-    @Override public JavaType getValueType() { return _type; }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/NullifyingDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/NullifyingDeserializer.java
index 0453595..bd58ecc 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/NullifyingDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/NullifyingDeserializer.java
@@ -9,7 +9,7 @@
 /**
  * Bogus deserializer that will simply skip all content there is to map
  * and returns Java null reference.
- * 
+ *
  * @since 2.2
  */
 public class NullifyingDeserializer
@@ -26,7 +26,12 @@
     /* Deserializer API
     /**********************************************************
      */
-    
+
+    @Override // since 2.9
+    public Boolean supportsUpdate(DeserializationConfig config) {
+        return Boolean.FALSE;
+    }
+
     @Override
     public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
     {
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/NumberDeserializers.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/NumberDeserializers.java
index e545d6b..35ec9d4 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/NumberDeserializers.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/NumberDeserializers.java
@@ -6,9 +6,11 @@
 import java.util.HashSet;
 
 import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.io.NumberInput;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
 import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.util.AccessPattern;
 
 /**
  * Container class for deserializers that handle core JDK primitive
@@ -120,35 +122,47 @@
         private static final long serialVersionUID = 1L;
 
         protected final T _nullValue;
+
+        // @since 2.9
+        protected final T _emptyValue;
+
         protected final boolean _primitive;
 
-        protected PrimitiveOrWrapperDeserializer(Class<T> vc, T nvl) {
+        protected PrimitiveOrWrapperDeserializer(Class<T> vc, T nvl, T empty) {
             super(vc);
             _nullValue = nvl;
+            _emptyValue = empty;
             _primitive = vc.isPrimitive();
         }
 
         @Override
-        public final T getNullValue(DeserializationContext ctxt) throws JsonMappingException
-        {
+        public AccessPattern getNullAccessPattern() {
+            // 02-Feb-2017, tatu: For primitives we must dynamically check (and possibly throw
+            //     exception); for wrappers not.
+            if (_primitive) {
+                return AccessPattern.DYNAMIC;
+            }
+            if (_nullValue == null) {
+                return AccessPattern.ALWAYS_NULL;
+            }
+            return AccessPattern.CONSTANT;
+        }
+
+        @Override
+        public final T getNullValue(DeserializationContext ctxt) throws JsonMappingException {
+            // 01-Mar-2017, tatu: Alas, not all paths lead to `_coerceNull()`, as `SettableBeanProperty`
+            //    short-circuits `null` handling. Hence need this check as well.
             if (_primitive && ctxt.isEnabled(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)) {
-                ctxt.reportMappingException(
-                        "Can not map JSON null into type %s (set DeserializationConfig.DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES to 'false' to allow)",
+                ctxt.reportInputMismatch(this,
+                        "Cannot map `null` into type %s (set DeserializationConfig.DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES to 'false' to allow)",
                         handledType().toString());
             }
             return _nullValue;
         }
 
         @Override
-        public T getEmptyValue(DeserializationContext ctxt) throws JsonMappingException {
-            // [databind#1095]: Should not allow coercion from into null from Empty String
-            // either, if `null` not allowed
-            if (_primitive && ctxt.isEnabled(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)) {
-                ctxt.reportMappingException(
-                        "Can not map Empty String as null into type %s (set DeserializationConfig.DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES to 'false' to allow)",
-                        handledType().toString());
-            }
-            return _nullValue;
+        public Object getEmptyValue(DeserializationContext ctxt) throws JsonMappingException {
+            return _emptyValue;
         }
     }
 
@@ -169,13 +183,20 @@
 
         public BooleanDeserializer(Class<Boolean> cls, Boolean nvl)
         {
-            super(cls, nvl);
+            super(cls, nvl, Boolean.FALSE);
         }
 
         @Override
-        public Boolean deserialize(JsonParser j, DeserializationContext ctxt) throws IOException
+        public Boolean deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
         {
-            return _parseBoolean(j, ctxt);
+            JsonToken t = p.getCurrentToken();
+            if (t == JsonToken.VALUE_TRUE) {
+                return Boolean.TRUE;
+            }
+            if (t == JsonToken.VALUE_FALSE) {
+                return Boolean.FALSE;
+            }
+            return _parseBoolean(p, ctxt);
         }
 
         // Since we can never have type info ("natural type"; String, Boolean, Integer, Double):
@@ -185,8 +206,61 @@
                 TypeDeserializer typeDeserializer)
             throws IOException
         {
+            JsonToken t = p.getCurrentToken();
+            if (t == JsonToken.VALUE_TRUE) {
+                return Boolean.TRUE;
+            }
+            if (t == JsonToken.VALUE_FALSE) {
+                return Boolean.FALSE;
+            }
             return _parseBoolean(p, ctxt);
         }
+
+        protected final Boolean _parseBoolean(JsonParser p, DeserializationContext ctxt)
+            throws IOException
+        {
+            JsonToken t = p.getCurrentToken();
+            if (t == JsonToken.VALUE_NULL) {
+                return (Boolean) _coerceNullToken(ctxt, _primitive);
+            }
+            if (t == JsonToken.START_ARRAY) { // unwrapping?
+                return _deserializeFromArray(p, ctxt);
+            }
+            // should accept ints too, (0 == false, otherwise true)
+            if (t == JsonToken.VALUE_NUMBER_INT) {
+                return Boolean.valueOf(_parseBooleanFromInt(p, ctxt));
+            }
+            // And finally, let's allow Strings to be converted too
+            if (t == JsonToken.VALUE_STRING) {
+                String text = p.getText().trim();
+                // [databind#422]: Allow aliases
+                if ("true".equals(text) || "True".equals(text)) {
+                    _verifyStringForScalarCoercion(ctxt, text);
+                    return Boolean.TRUE;
+                }
+                if ("false".equals(text) || "False".equals(text)) {
+                    _verifyStringForScalarCoercion(ctxt, text);
+                    return Boolean.FALSE;
+                }
+                if (text.length() == 0) {
+                    return (Boolean) _coerceEmptyString(ctxt, _primitive);
+                }
+                if (_hasTextualNull(text)) {
+                    return (Boolean) _coerceTextualNull(ctxt, _primitive);
+                }
+                return (Boolean) ctxt.handleWeirdStringValue(_valueClass, text,
+                        "only \"true\" or \"false\" recognized");
+            }
+            // usually caller should have handled but:
+            if (t == JsonToken.VALUE_TRUE) {
+                return Boolean.TRUE;
+            }
+            if (t == JsonToken.VALUE_FALSE) {
+                return Boolean.FALSE;
+            }
+            // Otherwise, no can do:
+            return (Boolean) ctxt.handleUnexpectedToken(_valueClass, p);
+        }
     }
 
     @JacksonStdImpl
@@ -200,14 +274,65 @@
         
         public ByteDeserializer(Class<Byte> cls, Byte nvl)
         {
-            super(cls, nvl);
+            super(cls, nvl, (byte) 0);
         }
 
         @Override
         public Byte deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
         {
+            if (p.hasToken(JsonToken.VALUE_NUMBER_INT)) {
+                return p.getByteValue();
+            }
             return _parseByte(p, ctxt);
         }
+
+        protected Byte _parseByte(JsonParser p, DeserializationContext ctxt) throws IOException
+        {
+            JsonToken t = p.getCurrentToken();
+            if (t == JsonToken.VALUE_STRING) { // let's do implicit re-parse
+                String text = p.getText().trim();
+                if (_hasTextualNull(text)) {
+                    return (Byte) _coerceTextualNull(ctxt, _primitive);
+                }
+                int len = text.length();
+                if (len == 0) {
+                    return (Byte) _coerceEmptyString(ctxt, _primitive);
+                }
+                _verifyStringForScalarCoercion(ctxt, text);
+                int value;
+                try {
+                    value = NumberInput.parseInt(text);
+                } catch (IllegalArgumentException iae) {
+                    return (Byte) ctxt.handleWeirdStringValue(_valueClass, text,
+                            "not a valid Byte value");
+                }
+                // So far so good: but does it fit?
+                // as per [JACKSON-804], allow range up to 255, inclusive
+                if (_byteOverflow(value)) {
+                    return (Byte) ctxt.handleWeirdStringValue(_valueClass, text,
+                            "overflow, value cannot be represented as 8-bit value");
+                    // fall-through for deferred fails
+                }
+                return Byte.valueOf((byte) value);
+            }
+            if (t == JsonToken.VALUE_NUMBER_FLOAT) {
+                if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_FLOAT_AS_INT)) {
+                    _failDoubleToIntCoercion(p, ctxt, "Byte");
+                }
+                return p.getByteValue();
+            }
+            if (t == JsonToken.VALUE_NULL) {
+                return (Byte) _coerceNullToken(ctxt, _primitive);
+            }
+            // [databind#381]
+            if (t == JsonToken.START_ARRAY) {
+                return _deserializeFromArray(p, ctxt);
+            }
+            if (t == JsonToken.VALUE_NUMBER_INT) { // shouldn't usually be called with it but
+                return p.getByteValue();
+            }
+            return (Byte) ctxt.handleUnexpectedToken(_valueClass, p);
+        }
     }
 
     @JacksonStdImpl
@@ -221,14 +346,59 @@
         
         public ShortDeserializer(Class<Short> cls, Short nvl)
         {
-            super(cls, nvl);
+            super(cls, nvl, (short)0);
         }
 
         @Override
-        public Short deserialize(JsonParser jp, DeserializationContext ctxt)
+        public Short deserialize(JsonParser p, DeserializationContext ctxt)
             throws IOException
         {
-            return _parseShort(jp, ctxt);
+            return _parseShort(p, ctxt);
+        }
+
+        protected Short _parseShort(JsonParser p, DeserializationContext ctxt) throws IOException
+        {
+            JsonToken t = p.getCurrentToken();
+            if (t == JsonToken.VALUE_NUMBER_INT) {
+                return p.getShortValue();
+            }
+            if (t == JsonToken.VALUE_STRING) { // let's do implicit re-parse
+                String text = p.getText().trim();
+                int len = text.length();
+                if (len == 0) {
+                    return (Short) _coerceEmptyString(ctxt, _primitive);
+                }
+                if (_hasTextualNull(text)) {
+                    return (Short) _coerceTextualNull(ctxt, _primitive);
+                }
+                _verifyStringForScalarCoercion(ctxt, text);
+                int value;
+                try {
+                    value = NumberInput.parseInt(text);
+                } catch (IllegalArgumentException iae) {
+                    return (Short) ctxt.handleWeirdStringValue(_valueClass, text,
+                            "not a valid Short value");
+                }
+                // So far so good: but does it fit?
+                if (_shortOverflow(value)) {
+                    return (Short) ctxt.handleWeirdStringValue(_valueClass, text,
+                            "overflow, value cannot be represented as 16-bit value");
+                }
+                return Short.valueOf((short) value);
+            }
+            if (t == JsonToken.VALUE_NUMBER_FLOAT) {
+                if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_FLOAT_AS_INT)) {
+                    _failDoubleToIntCoercion(p, ctxt, "Short");
+                }
+                return p.getShortValue();
+            }
+            if (t == JsonToken.VALUE_NULL) {
+                return (Short) _coerceNullToken(ctxt, _primitive);
+            }
+            if (t == JsonToken.START_ARRAY) {
+                return _deserializeFromArray(p, ctxt);
+            }
+            return (Short) ctxt.handleUnexpectedToken(_valueClass, p);
         }
     }
 
@@ -243,7 +413,7 @@
         
         public CharacterDeserializer(Class<Character> cls, Character nvl)
         {
-            super(cls, nvl);
+            super(cls, nvl, '\0');
         }
 
         @Override
@@ -252,6 +422,7 @@
         {
             switch (p.getCurrentTokenId()) {
             case JsonTokenId.ID_NUMBER_INT: // ok iff ascii value
+                _verifyNumberForScalarCoercion(ctxt, p);
                 int value = p.getIntValue();
                 if (value >= 0 && value <= 0xFFFF) {
                     return Character.valueOf((char) value);
@@ -265,9 +436,11 @@
                 }
                 // actually, empty should become null?
                 if (text.length() == 0) {
-                    return (Character) getEmptyValue(ctxt);
-                }               
+                    return (Character) _coerceEmptyString(ctxt, _primitive);
+                }
                 break;
+            case JsonTokenId.ID_NULL:
+                return (Character) _coerceNullToken(ctxt, _primitive);
             case JsonTokenId.ID_START_ARRAY:
                 return _deserializeFromArray(p, ctxt);
             default:
@@ -282,11 +455,11 @@
     {
         private static final long serialVersionUID = 1L;
 
-        final static IntegerDeserializer primitiveInstance = new IntegerDeserializer(Integer.TYPE, Integer.valueOf(0));
+        final static IntegerDeserializer primitiveInstance = new IntegerDeserializer(Integer.TYPE, 0);
         final static IntegerDeserializer wrapperInstance = new IntegerDeserializer(Integer.class, null);
         
         public IntegerDeserializer(Class<Integer> cls, Integer nvl) {
-            super(cls, nvl);
+            super(cls, nvl, 0);
         }
 
         // since 2.6, slightly faster lookups for this very common type
@@ -312,6 +485,51 @@
             }
             return _parseInteger(p, ctxt);
         }
+
+        protected final Integer _parseInteger(JsonParser p, DeserializationContext ctxt) throws IOException
+        {
+            switch (p.getCurrentTokenId()) {
+            // NOTE: caller assumed to usually check VALUE_NUMBER_INT in fast path
+            case JsonTokenId.ID_NUMBER_INT:
+                return Integer.valueOf(p.getIntValue());
+            case JsonTokenId.ID_NUMBER_FLOAT: // coercing may work too
+                if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_FLOAT_AS_INT)) {
+                    _failDoubleToIntCoercion(p, ctxt, "Integer");
+                }
+                return Integer.valueOf(p.getValueAsInt());
+            case JsonTokenId.ID_STRING: // let's do implicit re-parse
+                String text = p.getText().trim();
+                int len = text.length();
+                if (len == 0) {
+                    return (Integer) _coerceEmptyString(ctxt, _primitive);
+                }
+                if (_hasTextualNull(text)) {
+                    return (Integer) _coerceTextualNull(ctxt, _primitive);
+                }
+                _verifyStringForScalarCoercion(ctxt, text);
+                try {
+                    if (len > 9) {
+                        long l = Long.parseLong(text);
+                        if (_intOverflow(l)) {
+                            return (Integer) ctxt.handleWeirdStringValue(_valueClass, text, String.format(
+                                "Overflow: numeric value (%s) out of range of Integer (%d - %d)",
+                                text, Integer.MIN_VALUE, Integer.MAX_VALUE));
+                        }
+                        return Integer.valueOf((int) l);
+                    }
+                    return Integer.valueOf(NumberInput.parseInt(text));
+                } catch (IllegalArgumentException iae) {
+                    return (Integer) ctxt.handleWeirdStringValue(_valueClass, text,
+                            "not a valid Integer value");
+                }
+            case JsonTokenId.ID_NULL:
+                return (Integer) _coerceNullToken(ctxt, _primitive);
+            case JsonTokenId.ID_START_ARRAY:
+                return _deserializeFromArray(p, ctxt);
+            }
+            // Otherwise, no can do:
+            return (Integer) ctxt.handleUnexpectedToken(_valueClass, p);
+        }
     }
 
     @JacksonStdImpl
@@ -320,11 +538,11 @@
     {
         private static final long serialVersionUID = 1L;
 
-        final static LongDeserializer primitiveInstance = new LongDeserializer(Long.TYPE, Long.valueOf(0L));
+        final static LongDeserializer primitiveInstance = new LongDeserializer(Long.TYPE, 0L);
         final static LongDeserializer wrapperInstance = new LongDeserializer(Long.class, null);
         
         public LongDeserializer(Class<Long> cls, Long nvl) {
-            super(cls, nvl);
+            super(cls, nvl, 0L);
         }
 
         // since 2.6, slightly faster lookups for this very common type
@@ -338,6 +556,42 @@
             }
             return _parseLong(p, ctxt);
         }
+
+        protected final Long _parseLong(JsonParser p, DeserializationContext ctxt) throws IOException
+        {
+            switch (p.getCurrentTokenId()) {
+            // NOTE: caller assumed to usually check VALUE_NUMBER_INT in fast path
+            case JsonTokenId.ID_NUMBER_INT:
+                return p.getLongValue();
+            case JsonTokenId.ID_NUMBER_FLOAT:
+                if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_FLOAT_AS_INT)) {
+                    _failDoubleToIntCoercion(p, ctxt, "Long");
+                }
+                return p.getValueAsLong();
+            case JsonTokenId.ID_STRING:
+                String text = p.getText().trim();
+                if (text.length() == 0) {
+                    return (Long) _coerceEmptyString(ctxt, _primitive);
+                }
+                if (_hasTextualNull(text)) {
+                    return (Long) _coerceTextualNull(ctxt, _primitive);
+                }
+                _verifyStringForScalarCoercion(ctxt, text);
+                // let's allow Strings to be converted too
+                try {
+                    return Long.valueOf(NumberInput.parseLong(text));
+                } catch (IllegalArgumentException iae) { }
+                return (Long) ctxt.handleWeirdStringValue(_valueClass, text,
+                        "not a valid Long value");
+                // fall-through
+            case JsonTokenId.ID_NULL:
+                return (Long) _coerceNullToken(ctxt, _primitive);
+            case JsonTokenId.ID_START_ARRAY:
+                return _deserializeFromArray(p, ctxt);
+            }
+            // Otherwise, no can do:
+            return (Long) ctxt.handleUnexpectedToken(_valueClass, p);
+        }
     }
 
     @JacksonStdImpl
@@ -350,7 +604,7 @@
         final static FloatDeserializer wrapperInstance = new FloatDeserializer(Float.class, null);
         
         public FloatDeserializer(Class<Float> cls, Float nvl) {
-            super(cls, nvl);
+            super(cls, nvl, 0.f);
         }
 
         @Override
@@ -358,6 +612,58 @@
         {
             return _parseFloat(p, ctxt);
         }
+
+        protected final Float _parseFloat(JsonParser p, DeserializationContext ctxt)
+            throws IOException
+        {
+            // We accept couple of different types; obvious ones first:
+            JsonToken t = p.getCurrentToken();
+            
+            if (t == JsonToken.VALUE_NUMBER_FLOAT || t == JsonToken.VALUE_NUMBER_INT) { // coercing should work too
+                return p.getFloatValue();
+            }
+            // And finally, let's allow Strings to be converted too
+            if (t == JsonToken.VALUE_STRING) {
+                String text = p.getText().trim();
+                if ((text.length() == 0)) {
+                    return (Float) _coerceEmptyString(ctxt, _primitive);
+                }
+                if (_hasTextualNull(text)) {
+                    return (Float) _coerceTextualNull(ctxt, _primitive);
+                }
+                switch (text.charAt(0)) {
+                case 'I':
+                    if (_isPosInf(text)) {
+                        return Float.POSITIVE_INFINITY;
+                    }
+                    break;
+                case 'N':
+                    if (_isNaN(text)) {
+                        return Float.NaN;
+                    }
+                    break;
+                case '-':
+                    if (_isNegInf(text)) {
+                        return Float.NEGATIVE_INFINITY;
+                    }
+                    break;
+                }
+                _verifyStringForScalarCoercion(ctxt, text);
+                try {
+                    return Float.parseFloat(text);
+                } catch (IllegalArgumentException iae) { }
+                return (Float) ctxt.handleWeirdStringValue(_valueClass, text,
+                        "not a valid Float value");
+            }
+            if (t == JsonToken.VALUE_NULL) {
+                return (Float) _coerceNullToken(ctxt, _primitive);
+            }
+            if (t == JsonToken.START_ARRAY) {
+                return _deserializeFromArray(p, ctxt);
+            }
+            // Otherwise, no can do:
+            return (Float) ctxt.handleUnexpectedToken(_valueClass, p);
+        }
     }
 
     @JacksonStdImpl
@@ -370,21 +676,69 @@
         final static DoubleDeserializer wrapperInstance = new DoubleDeserializer(Double.class, null);
         
         public DoubleDeserializer(Class<Double> cls, Double nvl) {
-            super(cls, nvl);
+            super(cls, nvl, 0.d);
         }
 
         @Override
-        public Double deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
-            return _parseDouble(jp, ctxt);
+        public Double deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
+            return _parseDouble(p, ctxt);
         }
 
         // Since we can never have type info ("natural type"; String, Boolean, Integer, Double):
         // (is it an error to even call this version?)
         @Override
-        public Double deserializeWithType(JsonParser jp, DeserializationContext ctxt,
+        public Double deserializeWithType(JsonParser p, DeserializationContext ctxt,
                 TypeDeserializer typeDeserializer) throws IOException
         {
-            return _parseDouble(jp, ctxt);
+            return _parseDouble(p, ctxt);
+        }
+
+        protected final Double _parseDouble(JsonParser p, DeserializationContext ctxt) throws IOException
+        {
+            JsonToken t = p.getCurrentToken();
+            if (t == JsonToken.VALUE_NUMBER_INT || t == JsonToken.VALUE_NUMBER_FLOAT) { // coercing should work too
+                return p.getDoubleValue();
+            }
+            if (t == JsonToken.VALUE_STRING) {
+                String text = p.getText().trim();
+                if ((text.length() == 0)) {
+                    return (Double) _coerceEmptyString(ctxt, _primitive);
+                }
+                if (_hasTextualNull(text)) {
+                    return (Double) _coerceTextualNull(ctxt, _primitive);
+                }
+                switch (text.charAt(0)) {
+                case 'I':
+                    if (_isPosInf(text)) {
+                        return Double.POSITIVE_INFINITY;
+                    }
+                    break;
+                case 'N':
+                    if (_isNaN(text)) {
+                        return Double.NaN;
+                    }
+                    break;
+                case '-':
+                    if (_isNegInf(text)) {
+                        return Double.NEGATIVE_INFINITY;
+                    }
+                    break;
+                }
+                _verifyStringForScalarCoercion(ctxt, text);
+                try {
+                    return parseDouble(text);
+                } catch (IllegalArgumentException iae) { }
+                return (Double) ctxt.handleWeirdStringValue(_valueClass, text,
+                        "not a valid Double value");
+            }
+            if (t == JsonToken.VALUE_NULL) {
+                return (Double) _coerceNullToken(ctxt, _primitive);
+            }
+            if (t == JsonToken.START_ARRAY) {
+                return _deserializeFromArray(p, ctxt);
+            }
+            // Otherwise, no can do:
+            return (Double) ctxt.handleUnexpectedToken(_valueClass, p);
         }
     }
 
@@ -421,7 +775,10 @@
 
             case JsonTokenId.ID_NUMBER_FLOAT:
                 if (ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) {
-                    return p.getDecimalValue();
+                    // 10-Mar-2017, tatu: NaN and BigDecimal won't mix...
+                    if (!p.isNaN()) {
+                        return p.getDecimalValue();
+                    }
                 }
                 return p.getNumberValue();
 
@@ -430,10 +787,12 @@
                  * out 'minimal' type to use 
                  */
                 String text = p.getText().trim();
-                if (text.length() == 0) {
-                    return getEmptyValue(ctxt);
+                if ((text.length() == 0)) {
+                    // note: no need to call `coerce` as this is never primitive
+                    return getNullValue(ctxt);
                 }
                 if (_hasTextualNull(text)) {
+                    // note: no need to call `coerce` as this is never primitive
                     return getNullValue(ctxt);
                 }
                 if (_isPosInf(text)) {
@@ -445,12 +804,13 @@
                 if (_isNaN(text)) {
                     return Double.NaN;
                 }
+                _verifyStringForScalarCoercion(ctxt, text);
                 try {
                     if (!_isIntNumber(text)) {
                         if (ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) {
                             return new BigDecimal(text);
                         }
-                        return new Double(text);
+                        return Double.valueOf(text);
                     }
                     if (ctxt.isEnabled(DeserializationFeature.USE_BIG_INTEGER_FOR_INTS)) {
                         return new BigInteger(text);
@@ -480,18 +840,18 @@
          * calling type deserializer.
          */
         @Override
-        public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt,
-                                          TypeDeserializer typeDeserializer)
+        public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
+                TypeDeserializer typeDeserializer)
             throws IOException
         {
-            switch (jp.getCurrentTokenId()) {
+            switch (p.getCurrentTokenId()) {
             case JsonTokenId.ID_NUMBER_INT:
             case JsonTokenId.ID_NUMBER_FLOAT:
             case JsonTokenId.ID_STRING:
-                // can not point to type information: hence must be non-typed (int/double)
-                return deserialize(jp, ctxt);
+                // cannot point to type information: hence must be non-typed (int/double)
+                return deserialize(p, ctxt);
             }
-            return typeDeserializer.deserializeTypedFromScalar(jp, ctxt);
+            return typeDeserializer.deserializeTypedFromScalar(p, ctxt);
         }
     }
 
@@ -515,6 +875,11 @@
 
         public BigIntegerDeserializer() { super(BigInteger.class); }
 
+        @Override
+        public Object getEmptyValue(DeserializationContext ctxt) {
+            return BigInteger.ZERO;
+        }
+
         @SuppressWarnings("incomplete-switch")
         @Override
         public BigInteger deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
@@ -537,15 +902,17 @@
                 return _deserializeFromArray(p, ctxt);
             case JsonTokenId.ID_STRING: // let's do implicit re-parse
                 String text = p.getText().trim();
-                if (text.length() == 0) {
-                    return null;
+                // note: no need to call `coerce` as this is never primitive
+                if (_isEmptyOrTextualNull(text)) {
+                    _verifyNullForScalarCoercion(ctxt, text);
+                    return getNullValue(ctxt);
                 }
+                _verifyStringForScalarCoercion(ctxt, text);
                 try {
                     return new BigInteger(text);
-                } catch (IllegalArgumentException iae) {
-                    return (BigInteger) ctxt.handleWeirdStringValue(_valueClass, text,
-                            "not a valid representation");
-                }
+                } catch (IllegalArgumentException iae) { }
+                return (BigInteger) ctxt.handleWeirdStringValue(_valueClass, text,
+                        "not a valid representation");
             }
             // String is ok too, can easily convert; otherwise, no can do:
             return (BigInteger) ctxt.handleUnexpectedToken(_valueClass, p);
@@ -562,6 +929,11 @@
         public BigDecimalDeserializer() { super(BigDecimal.class); }
 
         @Override
+        public Object getEmptyValue(DeserializationContext ctxt) {
+            return BigDecimal.ZERO;
+        }
+        
+        @Override
         public BigDecimal deserialize(JsonParser p, DeserializationContext ctxt)
             throws IOException
         {
@@ -571,15 +943,17 @@
                 return p.getDecimalValue();
             case JsonTokenId.ID_STRING:
                 String text = p.getText().trim();
-                if (text.length() == 0) {
-                    return null;
+                // note: no need to call `coerce` as this is never primitive
+                if (_isEmptyOrTextualNull(text)) {
+                    _verifyNullForScalarCoercion(ctxt, text);
+                    return getNullValue(ctxt);
                 }
+                _verifyStringForScalarCoercion(ctxt, text);
                 try {
                     return new BigDecimal(text);
-                } catch (IllegalArgumentException iae) {
-                    return (BigDecimal) ctxt.handleWeirdStringValue(_valueClass, text,
-                            "not a valid representation");
-                }
+                } catch (IllegalArgumentException iae) { }
+                return (BigDecimal) ctxt.handleWeirdStringValue(_valueClass, text,
+                        "not a valid representation");
             case JsonTokenId.ID_START_ARRAY:
                 return _deserializeFromArray(p, ctxt);
             }
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/ObjectArrayDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/ObjectArrayDeserializer.java
index 101217b..017317d 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/ObjectArrayDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/ObjectArrayDeserializer.java
@@ -10,8 +10,9 @@
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
 import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
+import com.fasterxml.jackson.databind.deser.NullValueProvider;
 import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
-import com.fasterxml.jackson.databind.type.ArrayType;
+import com.fasterxml.jackson.databind.util.AccessPattern;
 import com.fasterxml.jackson.databind.util.ObjectBuffer;
 
 /**
@@ -24,14 +25,11 @@
 {
     private static final long serialVersionUID = 1L;
 
+    protected final static Object[] NO_OBJECTS = new Object[0];
+
     // // Configuration
 
     /**
-     * Full generic type of the array being deserialized
-     */
-    protected final ArrayType _arrayType;
-    
-    /**
      * Flag that indicates whether the component type is Object or not.
      * Used for minor optimization when constructing result.
      */
@@ -54,54 +52,42 @@
      */
     protected final TypeDeserializer _elementTypeDeserializer;
 
-    /**
-     * Specific override for this instance (from proper, or global per-type overrides)
-     * to indicate whether single value may be taken to mean an unwrapped one-element array
-     * or not. If null, left to global defaults.
-     *
-     * @since 2.7
-     */
-    protected final Boolean _unwrapSingle;
-
     /*
     /**********************************************************
     /* Life-cycle
     /**********************************************************
      */
 
-    public ObjectArrayDeserializer(ArrayType arrayType,
+    public ObjectArrayDeserializer(JavaType arrayType,
             JsonDeserializer<Object> elemDeser, TypeDeserializer elemTypeDeser)
     {
-        super(arrayType);
-        _arrayType = arrayType;
+        super(arrayType, null, null);
         _elementClass = arrayType.getContentType().getRawClass();
         _untyped = (_elementClass == Object.class);
         _elementDeserializer = elemDeser;
         _elementTypeDeserializer = elemTypeDeser;
-        _unwrapSingle = null;
     }
 
     protected ObjectArrayDeserializer(ObjectArrayDeserializer base,
             JsonDeserializer<Object> elemDeser, TypeDeserializer elemTypeDeser,
-            Boolean unwrapSingle)
+            NullValueProvider nuller, Boolean unwrapSingle)
     {
-        super(base._arrayType);
-        _arrayType = base._arrayType;
+        super(base, nuller, unwrapSingle);
         _elementClass = base._elementClass;
         _untyped = base._untyped;
 
         _elementDeserializer = elemDeser;
         _elementTypeDeserializer = elemTypeDeser;
-        _unwrapSingle = unwrapSingle;
     }
-    
+
     /**
      * Overridable fluent-factory method used to create contextual instances
      */
     public ObjectArrayDeserializer withDeserializer(TypeDeserializer elemTypeDeser,
             JsonDeserializer<?> elemDeser)
     {
-        return withResolved(elemTypeDeser, elemDeser, _unwrapSingle);
+        return withResolved(elemTypeDeser, elemDeser,
+                _nullProvider, _unwrapSingle);
     }
 
     /**
@@ -109,43 +95,46 @@
      */
     @SuppressWarnings("unchecked")
     public ObjectArrayDeserializer withResolved(TypeDeserializer elemTypeDeser,
-            JsonDeserializer<?> elemDeser, Boolean unwrapSingle)
+            JsonDeserializer<?> elemDeser, NullValueProvider nuller, Boolean unwrapSingle)
     {
-        if ((unwrapSingle == _unwrapSingle)
+        if ((unwrapSingle == _unwrapSingle) && (nuller == _nullProvider)
                 && (elemDeser == _elementDeserializer)
                 && (elemTypeDeser == _elementTypeDeserializer)) {
             return this;
         }
         return new ObjectArrayDeserializer(this,
-                (JsonDeserializer<Object>) elemDeser, elemTypeDeser, unwrapSingle);
+                (JsonDeserializer<Object>) elemDeser, elemTypeDeser,
+                nuller, unwrapSingle);
+    }
+
+    @Override // since 2.5
+    public boolean isCachable() {
+        // Important: do NOT cache if polymorphic values, or if there are annotation-based
+        // custom deserializers
+        return (_elementDeserializer == null) && (_elementTypeDeserializer == null);
     }
 
     @Override
     public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
             BeanProperty property) throws JsonMappingException
     {
-        JsonDeserializer<?> deser = _elementDeserializer;
-        Boolean unwrapSingle = findFormatFeature(ctxt, property, _arrayType.getRawClass(),
+        JsonDeserializer<?> valueDeser = _elementDeserializer;
+        Boolean unwrapSingle = findFormatFeature(ctxt, property, _containerType.getRawClass(),
                 JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
         // May have a content converter
-        deser = findConvertingContentDeserializer(ctxt, property, deser);
-        final JavaType vt = _arrayType.getContentType();
-        if (deser == null) {
-            deser = ctxt.findContextualValueDeserializer(vt, property);
+        valueDeser = findConvertingContentDeserializer(ctxt, property, valueDeser);
+        final JavaType vt = _containerType.getContentType();
+        if (valueDeser == null) {
+            valueDeser = ctxt.findContextualValueDeserializer(vt, property);
         } else { // if directly assigned, probably not yet contextual, so:
-            deser = ctxt.handleSecondaryContextualization(deser, property, vt);
+            valueDeser = ctxt.handleSecondaryContextualization(valueDeser, property, vt);
         }
         TypeDeserializer elemTypeDeser = _elementTypeDeserializer;
         if (elemTypeDeser != null) {
             elemTypeDeser = elemTypeDeser.forProperty(property);
         }
-        return withResolved(elemTypeDeser, deser, unwrapSingle);
-    }
-
-    @Override // since 2.5
-    public boolean isCachable() {
-        // Important: do NOT cache if polymorphic values, or ones with custom deserializer
-        return (_elementDeserializer == null) && (_elementTypeDeserializer == null);
+        NullValueProvider nuller = findContentNullProvider(ctxt, property, valueDeser);
+        return withResolved(elemTypeDeser, valueDeser, nuller, unwrapSingle);
     }
 
     /*
@@ -155,15 +144,22 @@
      */
 
     @Override
-    public JavaType getContentType() {
-        return _arrayType.getContentType();
-    }
-
-    @Override
     public JsonDeserializer<Object> getContentDeserializer() {
         return _elementDeserializer;
     }
-    
+
+    @Override // since 2.9
+    public AccessPattern getEmptyAccessPattern() {
+        // immutable, shareable so:
+        return AccessPattern.CONSTANT;
+    }
+
+    // need to override as we can't expose ValueInstantiator
+    @Override // since 2.9
+    public Object getEmptyValue(DeserializationContext ctxt) throws JsonMappingException {
+        return NO_OBJECTS;
+    }
+
     /*
     /**********************************************************
     /* JsonDeserializer API
@@ -191,7 +187,10 @@
                 Object value;
                 
                 if (t == JsonToken.VALUE_NULL) {
-                    value = _elementDeserializer.getNullValue(ctxt);
+                    if (_skipNullValues) {
+                        continue;
+                    }
+                    value = _nullProvider.getNullValue(ctxt);
                 } else if (typeDeser == null) {
                     value = _elementDeserializer.deserialize(p, ctxt);
                 } else {
@@ -223,12 +222,68 @@
             TypeDeserializer typeDeserializer)
         throws IOException
     {
-        /* Should there be separate handling for base64 stuff?
-         * for now this should be enough:
-         */
+        // Should there be separate handling for base64 stuff?
+        // for now this should be enough:
         return (Object[]) typeDeserializer.deserializeTypedFromArray(p, ctxt);
     }
-    
+
+    @Override // since 2.9
+    public Object[] deserialize(JsonParser p, DeserializationContext ctxt,
+            Object[] intoValue) throws IOException
+    {
+        if (!p.isExpectedStartArrayToken()) {
+            Object[] arr = handleNonArray(p, ctxt);
+            if (arr == null) {
+                return intoValue;
+            }
+            final int offset = intoValue.length;
+            Object[] result = new Object[offset + arr.length];
+            System.arraycopy(intoValue, 0, result, 0, offset);
+            System.arraycopy(arr, 0, result, offset, arr.length);
+            return result;
+        }
+
+        final ObjectBuffer buffer = ctxt.leaseObjectBuffer();
+        int ix = intoValue.length;
+        Object[] chunk = buffer.resetAndStart(intoValue, ix);
+        JsonToken t;
+        final TypeDeserializer typeDeser = _elementTypeDeserializer;
+
+        try {
+            while ((t = p.nextToken()) != JsonToken.END_ARRAY) {
+                Object value;
+                
+                if (t == JsonToken.VALUE_NULL) {
+                    if (_skipNullValues) {
+                        continue;
+                    }
+                    value = _nullProvider.getNullValue(ctxt);
+                } else if (typeDeser == null) {
+                    value = _elementDeserializer.deserialize(p, ctxt);
+                } else {
+                    value = _elementDeserializer.deserializeWithType(p, ctxt, typeDeser);
+                }
+                if (ix >= chunk.length) {
+                    chunk = buffer.appendCompletedChunk(chunk);
+                    ix = 0;
+                }
+                chunk[ix++] = value;
+            }
+        } catch (Exception e) {
+            throw JsonMappingException.wrapWithPath(e, chunk, buffer.bufferedSize() + ix);
+        }
+
+        Object[] result;
+
+        if (_untyped) {
+            result = buffer.completeAndClearBuffer(chunk, ix);
+        } else {
+            result = buffer.completeAndClearBuffer(chunk, ix, _elementClass);
+        }
+        ctxt.returnObjectBuffer(buffer);
+        return result;
+    }
+
     /*
     /**********************************************************
     /* Internal methods
@@ -259,7 +314,7 @@
                 return null;
             }
         }
-        
+
         // Can we do implicit coercion to a single-element array still?
         boolean canWrap = (_unwrapSingle == Boolean.TRUE) ||
                 ((_unwrapSingle == null) &&
@@ -272,13 +327,17 @@
                     && _elementClass == Byte.class) {
                 return deserializeFromBase64(p, ctxt);
             }
-            return (Object[]) ctxt.handleUnexpectedToken(_arrayType.getRawClass(), p);
+            return (Object[]) ctxt.handleUnexpectedToken(_containerType.getRawClass(), p);
         }
         JsonToken t = p.getCurrentToken();
         Object value;
         
         if (t == JsonToken.VALUE_NULL) {
-            value = _elementDeserializer.getNullValue(ctxt);
+            // 03-Feb-2017, tatu: Should this be skipped or not?
+            if (_skipNullValues) {
+                return NO_OBJECTS;
+            }
+            value = _nullProvider.getNullValue(ctxt);
         } else if (_elementTypeDeserializer == null) {
             value = _elementDeserializer.deserialize(p, ctxt);
         } else {
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers.java
index 4221b30..175db71 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers.java
@@ -1,13 +1,21 @@
 package com.fasterxml.jackson.databind.deser.std;
 
 import java.io.IOException;
+import java.lang.reflect.Array;
+import java.util.Arrays;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.Nulls;
 import com.fasterxml.jackson.core.*;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
 import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
+import com.fasterxml.jackson.databind.deser.NullValueProvider;
+import com.fasterxml.jackson.databind.deser.impl.NullsConstantProvider;
+import com.fasterxml.jackson.databind.deser.impl.NullsFailProvider;
+import com.fasterxml.jackson.databind.exc.InvalidNullException;
 import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.util.AccessPattern;
 import com.fasterxml.jackson.databind.util.ArrayBuilders;
 
 /**
@@ -27,18 +35,35 @@
      */
     protected final Boolean _unwrapSingle;
 
+    // since 2.9
+    private transient Object _emptyValue;
+
+    /**
+     * Flag that indicates need for special handling; either failing
+     * (throw exception) or skipping
+     */
+    protected final NullValueProvider _nuller;
+
+    /*
+    /********************************************************
+    /* Life-cycle
+    /********************************************************
+     */
+    
     protected PrimitiveArrayDeserializers(Class<T> cls) {
         super(cls);
         _unwrapSingle = null;
+        _nuller = null;
     }
 
     /**
      * @since 2.7
      */
     protected PrimitiveArrayDeserializers(PrimitiveArrayDeserializers<?> base,
-            Boolean unwrapSingle) {
+            NullValueProvider nuller, Boolean unwrapSingle) {
         super(base._valueClass);
         _unwrapSingle = unwrapSingle;
+        _nuller = nuller;
     }
     
     public static JsonDeserializer<?> forType(Class<?> rawType)
@@ -72,37 +97,134 @@
         throw new IllegalStateException();
     }
 
-    /**
-     * @since 2.7
-     */
-    protected abstract PrimitiveArrayDeserializers<?> withResolved(Boolean unwrapSingle);
-
     @Override
     public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
             BeanProperty property) throws JsonMappingException
     {
         Boolean unwrapSingle = findFormatFeature(ctxt, property, _valueClass,
                 JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
-        if (unwrapSingle == _unwrapSingle) {
+        NullValueProvider nuller = null;
+
+        Nulls nullStyle = findContentNullStyle(ctxt, property);
+        if (nullStyle == Nulls.SKIP) {
+            nuller = NullsConstantProvider.skipper();
+        } else if (nullStyle == Nulls.FAIL) {
+            if (property == null) {
+                nuller = NullsFailProvider.constructForRootValue(ctxt.constructType(_valueClass));
+            } else {
+                nuller = NullsFailProvider.constructForProperty(property);
+            }
+        }
+        if ((unwrapSingle == _unwrapSingle) && (nuller == _nuller)) {
             return this;
         }
-        return withResolved(unwrapSingle);
+        return withResolved(nuller, unwrapSingle);
+    }
+
+    /*
+    /********************************************************
+    /* Abstract methods for sub-classes to implement
+    /********************************************************
+     */
+
+    /**
+     * @since 2.9
+     */
+    protected abstract T _concat(T oldValue, T newValue);
+
+    protected abstract T handleSingleElementUnwrapped(JsonParser p,
+            DeserializationContext ctxt) throws IOException;
+
+    /**
+     * @since 2.9
+     */
+    protected abstract PrimitiveArrayDeserializers<?> withResolved(NullValueProvider nuller,
+            Boolean unwrapSingle);
+
+    // since 2.9
+    protected abstract T _constructEmpty();
+    
+    /*
+    /********************************************************
+    /* Default implementations
+    /********************************************************
+     */
+    
+    @Override // since 2.9
+    public Boolean supportsUpdate(DeserializationConfig config) {
+        return Boolean.TRUE;
+    }
+
+    @Override
+    public AccessPattern getEmptyAccessPattern() {
+        // Empty values shareable freely
+        return AccessPattern.CONSTANT;
+    }
+    
+    @Override // since 2.9
+    public Object getEmptyValue(DeserializationContext ctxt) throws JsonMappingException {
+        Object empty = _emptyValue;
+        if (empty == null) {
+            _emptyValue = empty = _constructEmpty();
+        }
+        return empty;
     }
 
     @Override
     public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
             TypeDeserializer typeDeserializer) throws IOException
     {
-        /* Should there be separate handling for base64 stuff?
-         * for now this should be enough:
-         */
+        // Should there be separate handling for base64 stuff?
+        // for now this should be enough:
         return typeDeserializer.deserializeTypedFromArray(p, ctxt);
     }
 
+    @Override
+    public T deserialize(JsonParser p, DeserializationContext ctxt, T existing) throws IOException
+    {
+        T newValue = deserialize(p, ctxt);
+        if (existing == null) {
+            return newValue;
+        }
+        int len = Array.getLength(existing);
+        if (len == 0) {
+            return newValue;
+        }
+        return _concat(existing, newValue);
+    }
+
+    /*
+    /********************************************************
+    /* Helper methods for sub-classes
+    /********************************************************
+     */
+    
+    /*
+     * Convenience method that constructs a concatenation of two arrays,
+     * with the type they have.
+     *
+     * @since 2.9
+    @SuppressWarnings("unchecked")
+    public static <T> T concatArrays(T array1, T array2)
+    {
+        int len1 = Array.getLength(array1);
+        if (len1 == 0) {
+            return array2;
+        }
+        int len2 = Array.getLength(array2);
+        if (len2 == 0) {
+            return array1;
+        }
+        Object result = Arrays.copyOf((Object[]) array1, len1 + len2);
+        System.arraycopy(array2, 0, result, len1, len2);
+        return (T) result;
+    }
+    */
+    
     @SuppressWarnings("unchecked")
     protected T handleNonArray(JsonParser p, DeserializationContext ctxt) throws IOException
     {
-        // [JACKSON-620] Empty String can become null...
+        // Empty String can become null...
         if (p.hasToken(JsonToken.VALUE_STRING)
                 && ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)) {
             if (p.getText().length() == 0) {
@@ -118,8 +240,10 @@
         return (T) ctxt.handleUnexpectedToken(_valueClass, p);
     }
 
-    protected abstract T handleSingleElementUnwrapped(JsonParser p,
-            DeserializationContext ctxt) throws IOException;
+    protected void _failOnNull(DeserializationContext ctxt) throws IOException
+    {
+        throw InvalidNullException.from(ctxt, null, ctxt.constructType(_valueClass));
+    }
 
     /*
     /********************************************************
@@ -134,16 +258,22 @@
         private static final long serialVersionUID = 1L;
 
         public CharDeser() { super(char[].class); }
-        protected CharDeser(CharDeser base, Boolean unwrapSingle) {
-            super(base, unwrapSingle);
+        protected CharDeser(CharDeser base, NullValueProvider nuller, Boolean unwrapSingle) {
+            super(base, nuller, unwrapSingle);
         }
 
         @Override
-        protected PrimitiveArrayDeserializers<?> withResolved(Boolean unwrapSingle) {
+        protected PrimitiveArrayDeserializers<?> withResolved(NullValueProvider nuller,
+                Boolean unwrapSingle) {
             // 11-Dec-2015, tatu: Not sure how re-wrapping would work; omit
             return this;
         }
-        
+
+        @Override
+        protected char[] _constructEmpty() {
+            return new char[0];
+        }
+
         @Override
         public char[] deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
         {
@@ -169,13 +299,20 @@
                     String str;
                     if (t == JsonToken.VALUE_STRING) {
                         str = p.getText();
+                    } else if (t == JsonToken.VALUE_NULL) {
+                        if (_nuller != null) {
+                            _nuller.getNullValue(ctxt);
+                            continue;
+                        }
+                        _verifyNullForPrimitive(ctxt);
+                        str = "\0";
                     } else {
                         CharSequence cs = (CharSequence) ctxt.handleUnexpectedToken(Character.TYPE, p);
                         str = cs.toString();
                     }
                     if (str.length() != 1) {
-                        ctxt.reportMappingException("Can not convert a JSON String of length %d into a char element of char array",
-                                str.length());
+                        ctxt.reportInputMismatch(this,
+"Cannot convert a JSON String of length %d into a char element of char array", str.length());
                     }
                     sb.append(str.charAt(0));
                 }
@@ -206,6 +343,15 @@
             // not sure how this should work...
             return (char[]) ctxt.handleUnexpectedToken(_valueClass, p);
         }
+
+        @Override
+        protected char[] _concat(char[] oldValue, char[] newValue) {
+            int len1 = oldValue.length;
+            int len2 = newValue.length;
+            char[] result = Arrays.copyOf(oldValue, len1+len2);
+            System.arraycopy(newValue, 0, result, len1, len2);
+            return result;
+        }
     }
 
     /*
@@ -221,18 +367,24 @@
         private static final long serialVersionUID = 1L;
 
         public BooleanDeser() { super(boolean[].class); }
-        protected BooleanDeser(BooleanDeser base, Boolean unwrapSingle) {
-            super(base, unwrapSingle);
+        protected BooleanDeser(BooleanDeser base, NullValueProvider nuller, Boolean unwrapSingle) {
+            super(base, nuller, unwrapSingle);
         }
 
         @Override
-        protected PrimitiveArrayDeserializers<?> withResolved(Boolean unwrapSingle) {
-            return new BooleanDeser(this, unwrapSingle);
+        protected PrimitiveArrayDeserializers<?> withResolved(NullValueProvider nuller,
+                Boolean unwrapSingle) {
+            return new BooleanDeser(this, nuller, unwrapSingle);
+        }
+
+        @Override
+        protected boolean[] _constructEmpty() {
+            return new boolean[0];
         }
 
         @Override
         public boolean[] deserialize(JsonParser p, DeserializationContext ctxt)
-            throws IOException, JsonProcessingException
+            throws IOException
         {
             if (!p.isExpectedStartArrayToken()) {
                 return handleNonArray(p, ctxt);
@@ -242,9 +394,23 @@
             int ix = 0;
 
             try {
-                while (p.nextToken() != JsonToken.END_ARRAY) {
-                    // whether we should allow truncating conversions?
-                    boolean value = _parseBooleanPrimitive(p, ctxt);
+                JsonToken t;
+                while ((t = p.nextToken()) != JsonToken.END_ARRAY) {
+                    boolean value;
+                    if (t == JsonToken.VALUE_TRUE) {
+                        value = true;
+                    } else if (t == JsonToken.VALUE_FALSE) {
+                        value = false;
+                    } else if (t == JsonToken.VALUE_NULL) {
+                        if (_nuller != null) {
+                            _nuller.getNullValue(ctxt);
+                            continue;
+                        }
+                        _verifyNullForPrimitive(ctxt);
+                        value = false;
+                    } else {
+                        value = _parseBooleanPrimitive(p, ctxt);
+                    }
                     if (ix >= chunk.length) {
                         chunk = builder.appendCompletedChunk(chunk, ix);
                         ix = 0;
@@ -262,6 +428,15 @@
                 DeserializationContext ctxt) throws IOException {
             return new boolean[] { _parseBooleanPrimitive(p, ctxt) };
         }
+
+        @Override
+        protected boolean[] _concat(boolean[] oldValue, boolean[] newValue) {
+            int len1 = oldValue.length;
+            int len2 = newValue.length;
+            boolean[] result = Arrays.copyOf(oldValue, len1+len2);
+            System.arraycopy(newValue, 0, result, len1, len2);
+            return result;
+        }
     }
 
     /**
@@ -275,13 +450,19 @@
         private static final long serialVersionUID = 1L;
 
         public ByteDeser() { super(byte[].class); }
-        protected ByteDeser(ByteDeser base, Boolean unwrapSingle) {
-            super(base, unwrapSingle);
+        protected ByteDeser(ByteDeser base, NullValueProvider nuller,Boolean unwrapSingle) {
+            super(base, nuller, unwrapSingle);
         }
 
         @Override
-        protected PrimitiveArrayDeserializers<?> withResolved(Boolean unwrapSingle) {
-            return new ByteDeser(this, unwrapSingle);
+        protected PrimitiveArrayDeserializers<?> withResolved(NullValueProvider nuller,
+                Boolean unwrapSingle) {
+            return new ByteDeser(this, nuller, unwrapSingle);
+        }
+
+        @Override
+        protected byte[] _constructEmpty() {
+            return new byte[0];
         }
 
         @Override
@@ -291,7 +472,19 @@
             
             // Most likely case: base64 encoded String?
             if (t == JsonToken.VALUE_STRING) {
-                return p.getBinaryValue(ctxt.getBase64Variant());
+                try {
+                    return p.getBinaryValue(ctxt.getBase64Variant());
+                } catch (JsonParseException e) {
+                    // 25-Nov-2016, tatu: related to [databind#1425], try to convert
+                    //   to a more usable one, as it's not really a JSON-level parse
+                    //   exception, but rather binding from JSON String into base64 decoded
+                    //   binary data
+                    String msg = e.getOriginalMessage();
+                    if (msg.contains("base64")) {
+                        return (byte[]) ctxt.handleWeirdStringValue(byte[].class,
+                                p.getText(), msg);
+                    }
+                }
             }
             // 31-Dec-2009, tatu: Also may be hidden as embedded Object
             if (t == JsonToken.VALUE_EMBEDDED_OBJECT) {
@@ -318,10 +511,14 @@
                     } else {
                         // should probably accept nulls as 0
                         if (t == JsonToken.VALUE_NULL) {
+                            if (_nuller != null) {
+                                _nuller.getNullValue(ctxt);
+                                continue;
+                            }
+                            _verifyNullForPrimitive(ctxt);
                             value = (byte) 0;
                         } else {
-                            Number n = (Number) ctxt.handleUnexpectedToken(_valueClass.getComponentType(), p);
-                            value = n.byteValue();
+                            value = _parseBytePrimitive(p, ctxt);
                         }
                     }
                     if (ix >= chunk.length) {
@@ -348,6 +545,11 @@
             } else {
                 // should probably accept nulls as 'false'
                 if (t == JsonToken.VALUE_NULL) {
+                    if (_nuller != null) {
+                        _nuller.getNullValue(ctxt);
+                        return (byte[]) getEmptyValue(ctxt);
+                    }
+                    _verifyNullForPrimitive(ctxt);
                     return null;
                 }
                 Number n = (Number) ctxt.handleUnexpectedToken(_valueClass.getComponentType(), p);
@@ -355,6 +557,15 @@
             }
             return new byte[] { value };
         }
+
+        @Override
+        protected byte[] _concat(byte[] oldValue, byte[] newValue) {
+            int len1 = oldValue.length;
+            int len2 = newValue.length;
+            byte[] result = Arrays.copyOf(oldValue, len1+len2);
+            System.arraycopy(newValue, 0, result, len1, len2);
+            return result;
+        }
     }
 
     @JacksonStdImpl
@@ -364,15 +575,21 @@
         private static final long serialVersionUID = 1L;
 
         public ShortDeser() { super(short[].class); }
-        protected ShortDeser(ShortDeser base, Boolean unwrapSingle) {
-            super(base, unwrapSingle);
+        protected ShortDeser(ShortDeser base, NullValueProvider nuller, Boolean unwrapSingle) {
+            super(base, nuller, unwrapSingle);
         }
 
         @Override
-        protected PrimitiveArrayDeserializers<?> withResolved(Boolean unwrapSingle) {
-            return new ShortDeser(this, unwrapSingle);
+        protected PrimitiveArrayDeserializers<?> withResolved(NullValueProvider nuller,
+                Boolean unwrapSingle) {
+            return new ShortDeser(this, nuller, unwrapSingle);
         }
-        
+
+        @Override
+        protected short[] _constructEmpty() {
+            return new short[0];
+        }
+
         @Override
         public short[] deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
         {
@@ -384,8 +601,19 @@
             int ix = 0;
 
             try {
-                while (p.nextToken() != JsonToken.END_ARRAY) {
-                    short value = _parseShortPrimitive(p, ctxt);
+                JsonToken t;
+                while ((t = p.nextToken()) != JsonToken.END_ARRAY) {
+                    short value;
+                    if (t == JsonToken.VALUE_NULL) {
+                        if (_nuller != null) {
+                            _nuller.getNullValue(ctxt);
+                            continue;
+                        }
+                        _verifyNullForPrimitive(ctxt);
+                        value = (short) 0;
+                    } else {
+                        value = _parseShortPrimitive(p, ctxt);
+                    }
                     if (ix >= chunk.length) {
                         chunk = builder.appendCompletedChunk(chunk, ix);
                         ix = 0;
@@ -403,6 +631,15 @@
                 DeserializationContext ctxt) throws IOException {
             return new short[] { _parseShortPrimitive(p, ctxt) };
         }
+
+        @Override
+        protected short[] _concat(short[] oldValue, short[] newValue) {
+            int len1 = oldValue.length;
+            int len2 = newValue.length;
+            short[] result = Arrays.copyOf(oldValue, len1+len2);
+            System.arraycopy(newValue, 0, result, len1, len2);
+            return result;
+        }
     }
 
     @JacksonStdImpl
@@ -414,13 +651,19 @@
         public final static IntDeser instance = new IntDeser();
         
         public IntDeser() { super(int[].class); }
-        protected IntDeser(IntDeser base, Boolean unwrapSingle) {
-            super(base, unwrapSingle);
+        protected IntDeser(IntDeser base, NullValueProvider nuller, Boolean unwrapSingle) {
+            super(base, nuller, unwrapSingle);
         }
 
         @Override
-        protected PrimitiveArrayDeserializers<?> withResolved(Boolean unwrapSingle) {
-            return new IntDeser(this, unwrapSingle);
+        protected PrimitiveArrayDeserializers<?> withResolved(NullValueProvider nuller,
+                Boolean unwrapSingle) {
+            return new IntDeser(this, nuller, unwrapSingle);
+        }
+
+        @Override
+        protected int[] _constructEmpty() {
+            return new int[0];
         }
 
         @Override
@@ -434,9 +677,21 @@
             int ix = 0;
 
             try {
-                while (p.nextToken() != JsonToken.END_ARRAY) {
-                    // whether we should allow truncating conversions?
-                    int value = _parseIntPrimitive(p, ctxt);
+                JsonToken t;
+                while ((t = p.nextToken()) != JsonToken.END_ARRAY) {
+                    int value;
+                    if (t == JsonToken.VALUE_NUMBER_INT) {
+                        value = p.getIntValue();
+                    } else if (t == JsonToken.VALUE_NULL) {
+                        if (_nuller != null) {
+                            _nuller.getNullValue(ctxt);
+                            continue;
+                        }
+                        _verifyNullForPrimitive(ctxt);
+                        value = 0;
+                    } else {
+                        value = _parseIntPrimitive(p, ctxt);
+                    }
                     if (ix >= chunk.length) {
                         chunk = builder.appendCompletedChunk(chunk, ix);
                         ix = 0;
@@ -454,6 +709,15 @@
                 DeserializationContext ctxt) throws IOException {
             return new int[] { _parseIntPrimitive(p, ctxt) };
         }
+
+        @Override
+        protected int[] _concat(int[] oldValue, int[] newValue) {
+            int len1 = oldValue.length;
+            int len2 = newValue.length;
+            int[] result = Arrays.copyOf(oldValue, len1+len2);
+            System.arraycopy(newValue, 0, result, len1, len2);
+            return result;
+        }
     }
 
     @JacksonStdImpl
@@ -465,13 +729,19 @@
         public final static LongDeser instance = new LongDeser();
 
         public LongDeser() { super(long[].class); }
-        protected LongDeser(LongDeser base, Boolean unwrapSingle) {
-            super(base, unwrapSingle);
+        protected LongDeser(LongDeser base, NullValueProvider nuller, Boolean unwrapSingle) {
+            super(base, nuller, unwrapSingle);
         }
 
         @Override
-        protected PrimitiveArrayDeserializers<?> withResolved(Boolean unwrapSingle) {
-            return new LongDeser(this, unwrapSingle);
+        protected PrimitiveArrayDeserializers<?> withResolved(NullValueProvider nuller,
+                Boolean unwrapSingle) {
+            return new LongDeser(this, nuller, unwrapSingle);
+        }
+
+        @Override
+        protected long[] _constructEmpty() {
+            return new long[0];
         }
 
         @Override
@@ -485,8 +755,21 @@
             int ix = 0;
 
             try {
-                while (p.nextToken() != JsonToken.END_ARRAY) {
-                    long value = _parseLongPrimitive(p, ctxt);
+                JsonToken t;
+                while ((t = p.nextToken()) != JsonToken.END_ARRAY) {
+                    long value;
+                    if (t == JsonToken.VALUE_NUMBER_INT) {
+                        value = p.getLongValue();
+                    } else if (t == JsonToken.VALUE_NULL) {
+                        if (_nuller != null) {
+                            _nuller.getNullValue(ctxt);
+                            continue;
+                        }
+                        _verifyNullForPrimitive(ctxt);
+                        value = 0L;
+                    } else {
+                        value = _parseLongPrimitive(p, ctxt);
+                    }
                     if (ix >= chunk.length) {
                         chunk = builder.appendCompletedChunk(chunk, ix);
                         ix = 0;
@@ -504,6 +787,15 @@
                 DeserializationContext ctxt) throws IOException {
             return new long[] { _parseLongPrimitive(p, ctxt) };
         }
+
+        @Override
+        protected long[] _concat(long[] oldValue, long[] newValue) {
+            int len1 = oldValue.length;
+            int len2 = newValue.length;
+            long[] result = Arrays.copyOf(oldValue, len1+len2);
+            System.arraycopy(newValue, 0, result, len1, len2);
+            return result;
+        }
     }
 
     @JacksonStdImpl
@@ -513,18 +805,23 @@
         private static final long serialVersionUID = 1L;
 
         public FloatDeser() { super(float[].class); }
-        protected FloatDeser(FloatDeser base, Boolean unwrapSingle) {
-            super(base, unwrapSingle);
+        protected FloatDeser(FloatDeser base, NullValueProvider nuller, Boolean unwrapSingle) {
+            super(base, nuller, unwrapSingle);
         }
 
         @Override
-        protected PrimitiveArrayDeserializers<?> withResolved(Boolean unwrapSingle) {
-            return new FloatDeser(this, unwrapSingle);
+        protected PrimitiveArrayDeserializers<?> withResolved(NullValueProvider nuller,
+                Boolean unwrapSingle) {
+            return new FloatDeser(this, nuller, unwrapSingle);
         }
 
         @Override
-        public float[] deserialize(JsonParser p, DeserializationContext ctxt)
-            throws IOException, JsonProcessingException
+        protected float[] _constructEmpty() {
+            return new float[0];
+        }
+
+        @Override
+        public float[] deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
         {
             if (!p.isExpectedStartArrayToken()) {
                 return handleNonArray(p, ctxt);
@@ -534,8 +831,15 @@
             int ix = 0;
 
             try {
-                while (p.nextToken() != JsonToken.END_ARRAY) {
+                JsonToken t;
+                while ((t = p.nextToken()) != JsonToken.END_ARRAY) {
                     // whether we should allow truncating conversions?
+                    if (t == JsonToken.VALUE_NULL) {
+                        if (_nuller != null) {
+                            _nuller.getNullValue(ctxt);
+                            continue;
+                        }
+                    }
                     float value = _parseFloatPrimitive(p, ctxt);
                     if (ix >= chunk.length) {
                         chunk = builder.appendCompletedChunk(chunk, ix);
@@ -554,6 +858,15 @@
                 DeserializationContext ctxt) throws IOException {
             return new float[] { _parseFloatPrimitive(p, ctxt) };
         }
+
+        @Override
+        protected float[] _concat(float[] oldValue, float[] newValue) {
+            int len1 = oldValue.length;
+            int len2 = newValue.length;
+            float[] result = Arrays.copyOf(oldValue, len1+len2);
+            System.arraycopy(newValue, 0, result, len1, len2);
+            return result;
+        }
     }
 
     @JacksonStdImpl
@@ -563,13 +876,19 @@
         private static final long serialVersionUID = 1L;
         
         public DoubleDeser() { super(double[].class); }
-        protected DoubleDeser(DoubleDeser base, Boolean unwrapSingle) {
-            super(base, unwrapSingle);
+        protected DoubleDeser(DoubleDeser base, NullValueProvider nuller, Boolean unwrapSingle) {
+            super(base, nuller, unwrapSingle);
         }
 
         @Override
-        protected PrimitiveArrayDeserializers<?> withResolved(Boolean unwrapSingle) {
-            return new DoubleDeser(this, unwrapSingle);
+        protected PrimitiveArrayDeserializers<?> withResolved(NullValueProvider nuller,
+                Boolean unwrapSingle) {
+            return new DoubleDeser(this, nuller, unwrapSingle);
+        }
+
+        @Override
+        protected double[] _constructEmpty() {
+            return new double[0];
         }
 
         @Override
@@ -583,7 +902,14 @@
             int ix = 0;
 
             try {
-                while (p.nextToken() != JsonToken.END_ARRAY) {
+                JsonToken t;
+                while ((t = p.nextToken()) != JsonToken.END_ARRAY) {
+                    if (t == JsonToken.VALUE_NULL) {
+                        if (_nuller != null) {
+                            _nuller.getNullValue(ctxt);
+                            continue;
+                        }
+                    }
                     double value = _parseDoublePrimitive(p, ctxt);
                     if (ix >= chunk.length) {
                         chunk = builder.appendCompletedChunk(chunk, ix);
@@ -602,5 +928,14 @@
                 DeserializationContext ctxt) throws IOException {
             return new double[] { _parseDoublePrimitive(p, ctxt) };
         }
+
+        @Override
+        protected double[] _concat(double[] oldValue, double[] newValue) {
+            int len1 = oldValue.length;
+            int len2 = newValue.length;
+            double[] result = Arrays.copyOf(oldValue, len1+len2);
+            System.arraycopy(newValue, 0, result, len1, len2);
+            return result;
+        }
     }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/ReferenceTypeDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/ReferenceTypeDeserializer.java
index 65876df..56bf081 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/ReferenceTypeDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/ReferenceTypeDeserializer.java
@@ -10,6 +10,7 @@
 import com.fasterxml.jackson.databind.deser.ValueInstantiator;
 import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
 import com.fasterxml.jackson.databind.type.ReferenceType;
+import com.fasterxml.jackson.databind.util.AccessPattern;
 
 /**
  * Base deserializer implementation for properties {@link ReferenceType} values.
@@ -22,17 +23,18 @@
     extends StdDeserializer<T>
     implements ContextualDeserializer
 {
-    private static final long serialVersionUID = 1L;
-    
+    private static final long serialVersionUID = 2L; // 2.9
+
     /**
      * Full type of property (or root value) for which this deserializer
      * has been constructed and contextualized.
      */
     protected final JavaType _fullType;
-    
+
+    protected final ValueInstantiator _valueInstantiator;
+
     protected final TypeDeserializer _valueTypeDeserializer;
-    
-    protected final JsonDeserializer<?> _valueDeserializer;
+    protected final JsonDeserializer<Object> _valueDeserializer;
 
     /*
     /**********************************************************
@@ -40,23 +42,27 @@
     /**********************************************************
      */
 
-    public ReferenceTypeDeserializer(JavaType fullType,
+    @SuppressWarnings("unchecked")
+    public ReferenceTypeDeserializer(JavaType fullType, ValueInstantiator vi,
             TypeDeserializer typeDeser, JsonDeserializer<?> deser)
     {
         super(fullType);
+        _valueInstantiator = vi;
         _fullType = fullType;
-        _valueDeserializer = deser;
+        _valueDeserializer = (JsonDeserializer<Object>) deser;
         _valueTypeDeserializer = typeDeser;
     }
 
-    // NOTE: for forwards-compatibility; added in 2.8.5 since 2.9.0 has it
-    public ReferenceTypeDeserializer(JavaType fullType, ValueInstantiator inst,
-            TypeDeserializer typeDeser, JsonDeserializer<?> deser) {
-        this(fullType, typeDeser, deser);
+    @Deprecated // since 2.9
+    public ReferenceTypeDeserializer(JavaType fullType,
+            TypeDeserializer typeDeser, JsonDeserializer<?> deser)
+    {
+        this(fullType, null, typeDeser, deser);
     }
 
     @Override
-    public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException
+    public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)
+            throws JsonMappingException
     {
         JsonDeserializer<?> deser = _valueDeserializer;
         if (deser == null) {
@@ -68,6 +74,7 @@
         if (typeDeser != null) {
             typeDeser = typeDeser.forProperty(property);
         }
+        // !!! 23-Oct-2016, tatu: TODO: full support for configurable ValueInstantiators?
         if ((deser == _valueDeserializer) && (typeDeser == _valueTypeDeserializer)) {
             return this;
         }
@@ -76,16 +83,68 @@
 
     /*
     /**********************************************************
-    /* Abstract methods for sub-classes to implement
+    /* Partial NullValueProvider impl
     /**********************************************************
      */
 
-    protected abstract ReferenceTypeDeserializer<T> withResolved(TypeDeserializer typeDeser, JsonDeserializer<?> valueDeser);
+    /**
+     * Null value varies dynamically (unlike with scalar types),
+     * so let's indicate this.
+     */
+    @Override
+    public AccessPattern getNullAccessPattern() {
+        return AccessPattern.DYNAMIC;
+    }
+
+    @Override
+    public AccessPattern getEmptyAccessPattern() {
+        return AccessPattern.DYNAMIC;
+    }
+
+    /*
+    /**********************************************************
+    /* Abstract methods for sub-classes to implement
+    /**********************************************************
+     */
+    
+    /**
+     * Mutant factory method called when changes are needed; should construct
+     * newly configured instance with new values as indicated.
+     *<p>
+     * NOTE: caller has verified that there are changes, so implementations
+     * need NOT check if a new instance is needed.
+     */
+    protected abstract ReferenceTypeDeserializer<T> withResolved(TypeDeserializer typeDeser,
+            JsonDeserializer<?> valueDeser);
 
     @Override
     public abstract T getNullValue(DeserializationContext ctxt);
 
+    @Override
+    public Object getEmptyValue(DeserializationContext ctxt) {
+        return getNullValue(ctxt);
+    }
+
     public abstract T referenceValue(Object contents);
+
+    /**
+     * Method called in case of "merging update", in which we should try
+     * update reference instead of creating a new one. If this does not
+     * succeed, should just create a new instance.
+     *
+     * @since 2.9
+     */
+    public abstract T updateReference(T reference, Object contents);
+
+    /**
+     * Method that may be called to find contents of specified reference,
+     * if any; or `null` if none. Note that method should never fail, so
+     * for types that use concept of "absence" vs "presence", `null` is
+     * to be returned for both "absent" and "reference to `null`" cases.
+     *
+     * @since 2.9
+     */
+    public abstract Object getReferenced(T reference);
     
     /*
     /**********************************************************
@@ -96,14 +155,32 @@
     @Override
     public JavaType getValueType() { return _fullType; }
 
+    /**
+     * By default we assume that updateability mostly relies on value
+     * deserializer; if it supports updates, typically that's what
+     * matters. So let's just delegate.
+     */
+    @Override // since 2.9
+    public Boolean supportsUpdate(DeserializationConfig config) {
+        return (_valueDeserializer == null) ? null
+                : _valueDeserializer.supportsUpdate(config);
+    }
+
     /*
     /**********************************************************
     /* Deserialization
     /**********************************************************
      */
-    
+
     @Override
     public T deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
+        // 23-Oct-2016, tatu: ValueInstantiator only defined for non-vanilla instances,
+        //    but do check... might work
+        if (_valueInstantiator != null) {
+            @SuppressWarnings("unchecked")
+            T value = (T) _valueInstantiator.createUsingDefault(ctxt);
+            return deserialize(p, ctxt, value);
+        }
         Object contents = (_valueTypeDeserializer == null)
                 ? _valueDeserializer.deserialize(p, ctxt)
                 : _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer);
@@ -111,6 +188,33 @@
     }
 
     @Override
+    public T deserialize(JsonParser p, DeserializationContext ctxt, T reference) throws IOException
+    {
+        Object contents;
+        // 26-Oct-2016, tatu: first things first; see if we should be able to merge:
+        Boolean B = _valueDeserializer.supportsUpdate(ctxt.getConfig());
+        // if explicitly stated that merge won't work...
+        if (B.equals(Boolean.FALSE) ||  (_valueTypeDeserializer != null)) {
+            contents = (_valueTypeDeserializer == null)
+                    ? _valueDeserializer.deserialize(p, ctxt)
+                    : _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer);
+        } else {
+            // Otherwise, see if we can merge the value
+            contents = getReferenced(reference);
+            // Whether to error or not... for now, just go back to default then
+            if (contents == null) {
+                contents = (_valueTypeDeserializer == null)
+                        ? _valueDeserializer.deserialize(p, ctxt)
+                        : _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer);
+                return referenceValue(contents);
+            } else {
+                contents = _valueDeserializer.deserialize(p, ctxt, contents);
+            }
+        }
+        return updateReference(reference, contents);
+    }
+
+    @Override
     public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
             TypeDeserializer typeDeserializer) throws IOException
     {
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/StackTraceElementDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StackTraceElementDeserializer.java
index a5decbe..fcfba10 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/StackTraceElementDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StackTraceElementDeserializer.java
@@ -24,6 +24,7 @@
             String className = "", methodName = "", fileName = "";
             // Java 9 adds couple more things
             String moduleName = null, moduleVersion = null;
+            String classLoaderName = null;
             int lineNumber = -1;
 
             while ((t = p.nextValue()) != JsonToken.END_OBJECT) {
@@ -31,6 +32,8 @@
                 // TODO: with Java 8, convert to switch
                 if ("className".equals(propName)) {
                     className = p.getText();
+                } else if ("classLoaderName".equals(propName)) {
+                    classLoaderName = p.getText();
                 } else if ("fileName".equals(propName)) {
                     fileName = p.getText();
                 } else if ("lineNumber".equals(propName)) {
@@ -47,12 +50,17 @@
                     moduleName = p.getText();
                 } else if ("moduleVersion".equals(propName)) {
                     moduleVersion = p.getText();
+                } else if ("declaringClass".equals(propName)
+                        || "format".equals(propName)) {
+                    // 01-Nov-2017: [databind#1794] Not sure if we should but... let's prune it for now
+                    ;
                 } else {
                     handleUnknownProperty(p, ctxt, _valueClass, propName);
                 }
+                p.skipChildren(); // just in case we might get structured values
             }
             return constructValue(ctxt, className, methodName, fileName, lineNumber,
-                    moduleName, moduleVersion);
+                    moduleName, moduleVersion, classLoaderName);
         } else if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
             p.nextToken();
             final StackTraceElement value = deserialize(p, ctxt);
@@ -64,6 +72,14 @@
         return (StackTraceElement) ctxt.handleUnexpectedToken(_valueClass, p);
     }
 
+    @Deprecated // since 2.9
+    protected StackTraceElement constructValue(DeserializationContext ctxt,
+            String className, String methodName, String fileName, int lineNumber,
+            String moduleName, String moduleVersion) {
+        return constructValue(ctxt, className, methodName, fileName, lineNumber,
+                moduleName, moduleVersion, null);
+    }
+
     /**
      * Overridable factory method used for constructing {@link StackTraceElement}s.
      *
@@ -71,7 +87,7 @@
      */
     protected StackTraceElement constructValue(DeserializationContext ctxt,
             String className, String methodName, String fileName, int lineNumber,
-            String moduleName, String moduleVersion)
+            String moduleName, String moduleVersion, String classLoaderName)
     {
         // 21-May-2016, tatu: With Java 9, need to use different constructor, probably
         //   via different module, and throw exception here if extra args passed
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDelegatingDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDelegatingDeserializer.java
index 9876124..0b7e0ed 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDelegatingDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDelegatingDeserializer.java
@@ -8,6 +8,7 @@
 import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
 import com.fasterxml.jackson.databind.deser.ResolvableDeserializer;
 import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.util.ClassUtil;
 import com.fasterxml.jackson.databind.util.Converter;
 
 /**
@@ -37,6 +38,9 @@
 {
     private static final long serialVersionUID = 1L;
 
+    /**
+     * Converter that was used for creating {@link #_delegateDeserializer}.
+     */
     protected final Converter<Object,T> _converter;
 
     /**
@@ -92,9 +96,7 @@
     protected StdDelegatingDeserializer<T> withDelegate(Converter<Object,T> converter,
             JavaType delegateType, JsonDeserializer<?> delegateDeserializer)
     {
-        if (getClass() != StdDelegatingDeserializer.class) {
-            throw new IllegalStateException("Sub-class "+getClass().getName()+" must override 'withDelegate'");
-        }
+        ClassUtil.verifyMustOverride(StdDelegatingDeserializer.class, this, "withDelegate");
         return new StdDelegatingDeserializer<T>(converter, delegateType, delegateDeserializer);
     }
 
@@ -119,7 +121,7 @@
     public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)
         throws JsonMappingException
     {
-        // First: if already got serializer to delegate to, contextualize it:
+        // First: if already got deserializer to delegate to, contextualize it:
         if (_delegateDeserializer != null) {
             JsonDeserializer<?> deser = ctxt.handleSecondaryContextualization(_delegateDeserializer,
                     property, _delegateType);
@@ -150,6 +152,11 @@
         return _delegateDeserializer.handledType();
     }
 
+    @Override // since 2.9
+    public Boolean supportsUpdate(DeserializationConfig config) {
+        return _delegateDeserializer.supportsUpdate(config);
+    }
+
     /*
     /**********************************************************
     /* Serialization
@@ -213,7 +220,7 @@
         throws IOException
     {
         throw new UnsupportedOperationException(String.format
-                ("Can not update object of type %s (using deserializer for type %s)"
+                ("Cannot update object of type %s (using deserializer for type %s)"
                         +intoValue.getClass().getName(), _delegateType));
     }
     
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDeserializer.java
index 70d7417..5d0133f 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDeserializer.java
@@ -4,12 +4,21 @@
 import java.util.*;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.Nulls;
 import com.fasterxml.jackson.core.*;
 import com.fasterxml.jackson.core.io.NumberInput;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
+import com.fasterxml.jackson.databind.deser.BeanDeserializerBase;
+import com.fasterxml.jackson.databind.deser.NullValueProvider;
+import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
+import com.fasterxml.jackson.databind.deser.ValueInstantiator;
+import com.fasterxml.jackson.databind.deser.impl.NullsAsEmptyProvider;
+import com.fasterxml.jackson.databind.deser.impl.NullsConstantProvider;
+import com.fasterxml.jackson.databind.deser.impl.NullsFailProvider;
 import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
 import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.util.AccessPattern;
 import com.fasterxml.jackson.databind.util.ClassUtil;
 import com.fasterxml.jackson.databind.util.Converter;
 
@@ -34,6 +43,12 @@
     protected final static int F_MASK_INT_COERCIONS = 
             DeserializationFeature.USE_BIG_INTEGER_FOR_INTS.getMask()
             | DeserializationFeature.USE_LONG_FOR_INTS.getMask();
+
+    // @since 2.9
+    protected final static int F_MASK_ACCEPT_ARRAYS =
+            DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS.getMask() |
+            DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT.getMask();
+
     
     /**
      * Type of values this deserializer handles: sometimes
@@ -48,7 +63,8 @@
     }
 
     protected StdDeserializer(JavaType valueType) {
-        _valueClass = (valueType == null) ? null : valueType.getRawClass();
+        // 26-Sep-2017, tatu: [databind#1764] need to add null-check back until 3.x
+        _valueClass = (valueType == null) ? Object.class : valueType.getRawClass();
     }
 
     /**
@@ -83,7 +99,7 @@
     public final Class<?> getValueClass() { return _valueClass; }
 
     /**
-     * Exact structured type deserializer handles, if known.
+     * Exact structured type this deserializer handles, if known.
      *<p>
      * Default implementation just returns null.
      */
@@ -119,7 +135,7 @@
             TypeDeserializer typeDeserializer) throws IOException {
         return typeDeserializer.deserializeTypedFromAny(p, ctxt);
     }
-    
+
     /*
     /**********************************************************
     /* Helper methods for sub-classes, parsing: while mostly
@@ -133,7 +149,10 @@
         JsonToken t = p.getCurrentToken();
         if (t == JsonToken.VALUE_TRUE) return true;
         if (t == JsonToken.VALUE_FALSE) return false;
-        if (t == JsonToken.VALUE_NULL) return false;
+        if (t == JsonToken.VALUE_NULL) {
+            _verifyNullForPrimitive(ctxt);
+            return false;
+        }
 
         // should accept ints too, (0 == false, otherwise true)
         if (t == JsonToken.VALUE_NUMBER_INT) {
@@ -146,80 +165,28 @@
             if ("true".equals(text) || "True".equals(text)) {
                 return true;
             }
-            if ("false".equals(text) || "False".equals(text) || text.length() == 0) {
+            if ("false".equals(text) || "False".equals(text)) {
                 return false;
             }
-            if (_hasTextualNull(text)) {
+            if (_isEmptyOrTextualNull(text)) {
+                _verifyNullForPrimitiveCoercion(ctxt, text);
                 return false;
             }
             Boolean b = (Boolean) ctxt.handleWeirdStringValue(_valueClass, text,
                     "only \"true\" or \"false\" recognized");
-            return (b == null) ? false : b.booleanValue();
+            return Boolean.TRUE.equals(b);
         }
         // [databind#381]
         if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
             p.nextToken();
             final boolean parsed = _parseBooleanPrimitive(p, ctxt);
-            t = p.nextToken();
-            if (t != JsonToken.END_ARRAY) {
-                handleMissingEndArrayForSingle(p, ctxt);
-            }            
+            _verifyEndArrayForSingle(p, ctxt);
             return parsed;            
         }
         // Otherwise, no can do:
         return ((Boolean) ctxt.handleUnexpectedToken(_valueClass, p)).booleanValue();
     }
 
-    protected final Boolean _parseBoolean(JsonParser p, DeserializationContext ctxt)
-        throws IOException
-    {
-        JsonToken t = p.getCurrentToken();
-        if (t == JsonToken.VALUE_TRUE) {
-            return Boolean.TRUE;
-        }
-        if (t == JsonToken.VALUE_FALSE) {
-            return Boolean.FALSE;
-        }
-        // should accept ints too, (0 == false, otherwise true)
-        if (t == JsonToken.VALUE_NUMBER_INT) {
-            return Boolean.valueOf(_parseBooleanFromInt(p, ctxt));
-        }
-        if (t == JsonToken.VALUE_NULL) {
-            return (Boolean) getNullValue(ctxt);
-        }
-        // And finally, let's allow Strings to be converted too
-        if (t == JsonToken.VALUE_STRING) {
-            String text = p.getText().trim();
-            // [databind#422]: Allow aliases
-            if ("true".equals(text) || "True".equals(text)) {
-                return Boolean.TRUE;
-            }
-            if ("false".equals(text) || "False".equals(text)) {
-                return Boolean.FALSE;
-            }
-            if (text.length() == 0) {
-                return (Boolean) getEmptyValue(ctxt);
-            }
-            if (_hasTextualNull(text)) {
-                return (Boolean) getNullValue(ctxt);
-            }
-            return (Boolean) ctxt.handleWeirdStringValue(_valueClass, text,
-                    "only \"true\" or \"false\" recognized");
-        }
-        // [databind#381]
-        if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
-            p.nextToken();
-            final Boolean parsed = _parseBoolean(p, ctxt);
-            t = p.nextToken();
-            if (t != JsonToken.END_ARRAY) {
-                handleMissingEndArrayForSingle(p, ctxt);
-            }            
-            return parsed;            
-        }
-        // Otherwise, no can do:
-        return (Boolean) ctxt.handleUnexpectedToken(_valueClass, p);
-    }
-
     protected boolean _parseBooleanFromInt(JsonParser p, DeserializationContext ctxt)
         throws IOException
     {
@@ -227,123 +194,23 @@
         //    degenerate case of huge integers, legal in JSON.
         //  ... this is, on the other hand, probably wrong/sub-optimal for non-JSON
         //  input. For now, no rea
-
+        _verifyNumberForScalarCoercion(ctxt, p);
         // Anyway, note that since we know it's valid (JSON) integer, it can't have
         // extra whitespace to trim.
         return !"0".equals(p.getText());
     }
 
-    @Deprecated // since 2.8.4
-    protected boolean _parseBooleanFromOther(JsonParser p, DeserializationContext ctxt)
+    protected final byte _parseBytePrimitive(JsonParser p, DeserializationContext ctxt)
         throws IOException
     {
-        return _parseBooleanFromInt(p, ctxt);
-    }
-
-    protected Byte _parseByte(JsonParser p, DeserializationContext ctxt)
-        throws IOException
-    {
-        JsonToken t = p.getCurrentToken();
-        if (t == JsonToken.VALUE_NUMBER_INT) {
-            return p.getByteValue();
+        int value = _parseIntPrimitive(p, ctxt);
+        // So far so good: but does it fit?
+        if (_byteOverflow(value)) {
+            Number v = (Number) ctxt.handleWeirdStringValue(_valueClass, String.valueOf(value),
+                    "overflow, value cannot be represented as 8-bit value");
+            return _nonNullNumber(v).byteValue();
         }
-        if (t == JsonToken.VALUE_STRING) { // let's do implicit re-parse
-            String text = p.getText().trim();
-            if (_hasTextualNull(text)) {
-                return (Byte) getNullValue(ctxt);
-            }
-            int value;
-            try {
-                int len = text.length();
-                if (len == 0) {
-                    return (Byte) getEmptyValue(ctxt);
-                }
-                value = NumberInput.parseInt(text);
-            } catch (IllegalArgumentException iae) {
-                return (Byte) ctxt.handleWeirdStringValue(_valueClass, text,
-                        "not a valid Byte value");
-            }
-            // So far so good: but does it fit?
-            // as per [JACKSON-804], allow range up to 255, inclusive
-            if (value < Byte.MIN_VALUE || value > 255) {
-                return (Byte) ctxt.handleWeirdStringValue(_valueClass, text,
-                        "overflow, value can not be represented as 8-bit value");
-                // fall-through for deferred fails
-            }
-            return Byte.valueOf((byte) value);
-        }
-        if (t == JsonToken.VALUE_NUMBER_FLOAT) {
-            if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_FLOAT_AS_INT)) {
-                _failDoubleToIntCoercion(p, ctxt, "Byte");
-            }
-            return p.getByteValue();
-        }
-        if (t == JsonToken.VALUE_NULL) {
-            return (Byte) getNullValue(ctxt);
-        }
-        // [databind#381]
-        if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
-            p.nextToken();
-            final Byte parsed = _parseByte(p, ctxt);
-            t = p.nextToken();
-            if (t != JsonToken.END_ARRAY) {
-                handleMissingEndArrayForSingle(p, ctxt);
-            }            
-            return parsed;            
-        }
-        return (Byte) ctxt.handleUnexpectedToken(_valueClass, p);
-    }
-    
-    protected Short _parseShort(JsonParser p, DeserializationContext ctxt)
-        throws IOException
-    {
-        JsonToken t = p.getCurrentToken();
-        if (t == JsonToken.VALUE_NUMBER_INT) {
-            return p.getShortValue();
-        }
-        if (t == JsonToken.VALUE_STRING) { // let's do implicit re-parse
-            String text = p.getText().trim();
-            int value;
-            try {
-                int len = text.length();
-                if (len == 0) {
-                    return (Short) getEmptyValue(ctxt);
-                }
-                if (_hasTextualNull(text)) {
-                    return (Short) getNullValue(ctxt);
-                }
-                value = NumberInput.parseInt(text);
-            } catch (IllegalArgumentException iae) {
-                return (Short) ctxt.handleWeirdStringValue(_valueClass, text,
-                        "not a valid Short value");
-            }
-            // So far so good: but does it fit?
-            if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) {
-                return (Short) ctxt.handleWeirdStringValue(_valueClass, text,
-                        "overflow, value can not be represented as 16-bit value");
-            }
-            return Short.valueOf((short) value);
-        }
-        if (t == JsonToken.VALUE_NUMBER_FLOAT) {
-            if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_FLOAT_AS_INT)) {
-                _failDoubleToIntCoercion(p, ctxt, "Short");
-            }
-            return p.getShortValue();
-        }
-        if (t == JsonToken.VALUE_NULL) {
-            return (Short) getNullValue(ctxt);
-        }
-        // [databind#381]
-        if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
-            p.nextToken();
-            final Short parsed = _parseShort(p, ctxt);
-            t = p.nextToken();
-            if (t != JsonToken.END_ARRAY) {
-                handleMissingEndArrayForSingle(p, ctxt);
-            }            
-            return parsed;            
-        }
-        return (Short) ctxt.handleUnexpectedToken(_valueClass, p);
+        return (byte) value;
     }
 
     protected final short _parseShortPrimitive(JsonParser p, DeserializationContext ctxt)
@@ -351,10 +218,10 @@
     {
         int value = _parseIntPrimitive(p, ctxt);
         // So far so good: but does it fit?
-        if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) {
+        if (_shortOverflow(value)) {
             Number v = (Number) ctxt.handleWeirdStringValue(_valueClass, String.valueOf(value),
-                    "overflow, value can not be represented as 16-bit value");
-            return (v == null) ? (short) 0 : v.shortValue();
+                    "overflow, value cannot be represented as 16-bit value");
+            return _nonNullNumber(v).shortValue();
         }
         return (short) value;
     }
@@ -365,189 +232,87 @@
         if (p.hasToken(JsonToken.VALUE_NUMBER_INT)) {
             return p.getIntValue();
         }
-        JsonToken t = p.getCurrentToken();
-        if (t == JsonToken.VALUE_STRING) { // let's do implicit re-parse
+        switch (p.getCurrentTokenId()) {
+        case JsonTokenId.ID_STRING:
             String text = p.getText().trim();
-            if (_hasTextualNull(text)) {
+            if (_isEmptyOrTextualNull(text)) {
+                _verifyNullForPrimitiveCoercion(ctxt, text);
                 return 0;
             }
-            try {
-                int len = text.length();
-                if (len > 9) {
-                    long l = Long.parseLong(text);
-                    if (l < Integer.MIN_VALUE || l > Integer.MAX_VALUE) {
-                        Number v = (Number) ctxt.handleWeirdStringValue(_valueClass, text,
-                            "Overflow: numeric value (%s) out of range of int (%d -%d)",
-                            text, Integer.MIN_VALUE, Integer.MAX_VALUE);
-                        return (v == null) ? 0 : v.intValue();
-                    }
-                    return (int) l;
-                }
-                if (len == 0) {
-                    return 0;
-                }
-                return NumberInput.parseInt(text);
-            } catch (IllegalArgumentException iae) {
-                Number v = (Number) ctxt.handleWeirdStringValue(_valueClass, text,
-                        "not a valid int value");
-                return (v == null) ? 0 : v.intValue();
-            }
-        }
-        if (t == JsonToken.VALUE_NUMBER_FLOAT) {
+            return _parseIntPrimitive(ctxt, text);
+        case JsonTokenId.ID_NUMBER_FLOAT:
             if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_FLOAT_AS_INT)) {
                 _failDoubleToIntCoercion(p, ctxt, "int");
             }
             return p.getValueAsInt();
-        }
-        if (t == JsonToken.VALUE_NULL) {
+        case JsonTokenId.ID_NULL:
+            _verifyNullForPrimitive(ctxt);
             return 0;
-        }
-        if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
-            p.nextToken();
-            final int parsed = _parseIntPrimitive(p, ctxt);
-            t = p.nextToken();
-            if (t != JsonToken.END_ARRAY) {
-                handleMissingEndArrayForSingle(p, ctxt);
-            }            
-            return parsed;            
+        case JsonTokenId.ID_START_ARRAY:
+            if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
+                p.nextToken();
+                final int parsed = _parseIntPrimitive(p, ctxt);
+                _verifyEndArrayForSingle(p, ctxt);
+                return parsed;            
+            }
+            break;
+        default:
         }
         // Otherwise, no can do:
         return ((Number) ctxt.handleUnexpectedToken(_valueClass, p)).intValue();
     }
 
-    protected final Integer _parseInteger(JsonParser p, DeserializationContext ctxt)
-        throws IOException
+    /**
+     * @since 2.9
+     */
+    protected final int _parseIntPrimitive(DeserializationContext ctxt, String text) throws IOException
     {
-        switch (p.getCurrentTokenId()) {
-        // NOTE: caller assumed to usually check VALUE_NUMBER_INT in fast path
-        case JsonTokenId.ID_NUMBER_INT:
-            return Integer.valueOf(p.getIntValue());
-        case JsonTokenId.ID_NUMBER_FLOAT: // coercing may work too
-            if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_FLOAT_AS_INT)) {
-                _failDoubleToIntCoercion(p, ctxt, "Integer");
-            }
-            return Integer.valueOf(p.getValueAsInt());
-        case JsonTokenId.ID_STRING: // let's do implicit re-parse
-            String text = p.getText().trim();
-            try {
-                int len = text.length();
-                if (_hasTextualNull(text)) {
-                    return (Integer) getNullValue(ctxt);
+        try {
+            if (text.length() > 9) {
+                long l = Long.parseLong(text);
+                if (_intOverflow(l)) {
+                    Number v = (Number) ctxt.handleWeirdStringValue(_valueClass, text,
+                        "Overflow: numeric value (%s) out of range of int (%d -%d)",
+                        text, Integer.MIN_VALUE, Integer.MAX_VALUE);
+                    return _nonNullNumber(v).intValue();
                 }
-                if (len > 9) {
-                    long l = Long.parseLong(text);
-                    if (l < Integer.MIN_VALUE || l > Integer.MAX_VALUE) {
-                        return (Integer) ctxt.handleWeirdStringValue(_valueClass, text,
-                            "Overflow: numeric value ("+text+") out of range of Integer ("+Integer.MIN_VALUE+" - "+Integer.MAX_VALUE+")");
-                        // fall-through
-                    }
-                    return Integer.valueOf((int) l);
-                }
-                if (len == 0) {
-                    return (Integer) getEmptyValue(ctxt);
-                }
-                return Integer.valueOf(NumberInput.parseInt(text));
-            } catch (IllegalArgumentException iae) {
-                return (Integer) ctxt.handleWeirdStringValue(_valueClass, text,
-                        "not a valid Integer value");
+                return (int) l;
             }
-            // fall-through
-        case JsonTokenId.ID_NULL:
-            return (Integer) getNullValue(ctxt);
-        case JsonTokenId.ID_START_ARRAY:
-            if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
-                p.nextToken();
-                final Integer parsed = _parseInteger(p, ctxt);
-                if (p.nextToken() != JsonToken.END_ARRAY) {
-                    handleMissingEndArrayForSingle(p, ctxt);
-                }            
-                return parsed;            
-            }
-            break;
+            return NumberInput.parseInt(text);
+        } catch (IllegalArgumentException iae) {
+            Number v = (Number) ctxt.handleWeirdStringValue(_valueClass, text,
+                    "not a valid int value");
+            return _nonNullNumber(v).intValue();
         }
-        // Otherwise, no can do:
-        return (Integer) ctxt.handleUnexpectedToken(_valueClass, p);
     }
-
-    protected final Long _parseLong(JsonParser p, DeserializationContext ctxt) throws IOException
-    {
-        switch (p.getCurrentTokenId()) {
-        // NOTE: caller assumed to usually check VALUE_NUMBER_INT in fast path
-        case JsonTokenId.ID_NUMBER_INT:
-            return p.getLongValue();
-        case JsonTokenId.ID_NUMBER_FLOAT:
-            if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_FLOAT_AS_INT)) {
-                _failDoubleToIntCoercion(p, ctxt, "Long");
-            }
-            return p.getValueAsLong();
-        case JsonTokenId.ID_STRING:
-            // let's allow Strings to be converted too
-            // !!! 05-Jan-2009, tatu: Should we try to limit value space, JDK is too lenient?
-            String text = p.getText().trim();
-            if (text.length() == 0) {
-                return (Long) getEmptyValue(ctxt);
-            }
-            if (_hasTextualNull(text)) {
-                return (Long) getNullValue(ctxt);
-            }
-            try {
-                return Long.valueOf(NumberInput.parseLong(text));
-            } catch (IllegalArgumentException iae) { }
-            return (Long) ctxt.handleWeirdStringValue(_valueClass, text,
-                    "not a valid Long value");
-            // fall-through
-        case JsonTokenId.ID_NULL:
-            return (Long) getNullValue(ctxt);
-        case JsonTokenId.ID_START_ARRAY:
-            if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
-                p.nextToken();
-                final Long parsed = _parseLong(p, ctxt);
-                JsonToken t = p.nextToken();
-                if (t != JsonToken.END_ARRAY) {
-                    handleMissingEndArrayForSingle(p, ctxt);
-                }            
-                return parsed;            
-            }
-            break;
-        }
-        // Otherwise, no can do:
-        return (Long) ctxt.handleUnexpectedToken(_valueClass, p);
-    }
-
+    
     protected final long _parseLongPrimitive(JsonParser p, DeserializationContext ctxt)
         throws IOException
     {
-        switch (p.getCurrentTokenId()) {
-        case JsonTokenId.ID_NUMBER_INT:
+        if (p.hasToken(JsonToken.VALUE_NUMBER_INT)) {
             return p.getLongValue();
+        }
+        switch (p.getCurrentTokenId()) {
+        case JsonTokenId.ID_STRING:
+            String text = p.getText().trim();
+            if (_isEmptyOrTextualNull(text)) {
+                _verifyNullForPrimitiveCoercion(ctxt, text);
+                return 0L;
+            }
+            return _parseLongPrimitive(ctxt, text);
         case JsonTokenId.ID_NUMBER_FLOAT:
             if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_FLOAT_AS_INT)) {
                 _failDoubleToIntCoercion(p, ctxt, "long");
             }
             return p.getValueAsLong();
-        case JsonTokenId.ID_STRING:
-            String text = p.getText().trim();
-            if (text.length() == 0 || _hasTextualNull(text)) {
-                return 0L;
-            }
-            try {
-                return NumberInput.parseLong(text);
-            } catch (IllegalArgumentException iae) { }
-            {
-                Number v = (Number) ctxt.handleWeirdStringValue(_valueClass, text,
-                        "not a valid long value");
-                return (v == null) ? 0 : v.longValue();
-            }
         case JsonTokenId.ID_NULL:
+            _verifyNullForPrimitive(ctxt);
             return 0L;
         case JsonTokenId.ID_START_ARRAY:
             if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
                 p.nextToken();
                 final long parsed = _parseLongPrimitive(p, ctxt);
-                JsonToken t = p.nextToken();
-                if (t != JsonToken.END_ARRAY) {
-                    handleMissingEndArrayForSingle(p, ctxt);
-                }
+                _verifyEndArrayForSingle(p, ctxt);
                 return parsed;
             }
             break;
@@ -555,249 +320,192 @@
         return ((Number) ctxt.handleUnexpectedToken(_valueClass, p)).longValue();
     }
 
-    protected final Float _parseFloat(JsonParser p, DeserializationContext ctxt)
-        throws IOException
+    /**
+     * @since 2.9
+     */
+    protected final long _parseLongPrimitive(DeserializationContext ctxt, String text) throws IOException
     {
-        // We accept couple of different types; obvious ones first:
-        JsonToken t = p.getCurrentToken();
-        
-        if (t == JsonToken.VALUE_NUMBER_INT || t == JsonToken.VALUE_NUMBER_FLOAT) { // coercing should work too
-            return p.getFloatValue();
+        try {
+            return NumberInput.parseLong(text);
+        } catch (IllegalArgumentException iae) { }
+        {
+            Number v = (Number) ctxt.handleWeirdStringValue(_valueClass, text,
+                    "not a valid long value");
+            return _nonNullNumber(v).longValue();
         }
-        // And finally, let's allow Strings to be converted too
-        if (t == JsonToken.VALUE_STRING) {
-            String text = p.getText().trim();
-            if (text.length() == 0) {
-                return (Float) getEmptyValue(ctxt);
-            }
-            if (_hasTextualNull(text)) {
-                return (Float) getNullValue(ctxt);
-            }
-            switch (text.charAt(0)) {
-            case 'I':
-                if (_isPosInf(text)) {
-                    return Float.POSITIVE_INFINITY;
-                }
-                break;
-            case 'N':
-                if (_isNaN(text)) {
-                    return Float.NaN;
-                }
-                break;
-            case '-':
-                if (_isNegInf(text)) {
-                    return Float.NEGATIVE_INFINITY;
-                }
-                break;
-            }
-            try {
-                return Float.parseFloat(text);
-            } catch (IllegalArgumentException iae) { }
-            return (Float) ctxt.handleWeirdStringValue(_valueClass, text,
-                    "not a valid Float value");
-        }
-        if (t == JsonToken.VALUE_NULL) {
-            return (Float) getNullValue(ctxt);
-        }
-        if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
-            p.nextToken();
-            final Float parsed = _parseFloat(p, ctxt);
-            t = p.nextToken();
-            if (t != JsonToken.END_ARRAY) {
-                handleMissingEndArrayForSingle(p, ctxt);
-            }            
-            return parsed;            
-        }
-        // Otherwise, no can do:
-        return (Float) ctxt.handleUnexpectedToken(_valueClass, p);
     }
 
     protected final float _parseFloatPrimitive(JsonParser p, DeserializationContext ctxt)
         throws IOException
     {
-        JsonToken t = p.getCurrentToken();
-
-        if (t == JsonToken.VALUE_NUMBER_INT || t == JsonToken.VALUE_NUMBER_FLOAT) { // coercing should work too
+        if (p.hasToken(JsonToken.VALUE_NUMBER_FLOAT)) {
             return p.getFloatValue();
         }
-        if (t == JsonToken.VALUE_STRING) {
+        switch (p.getCurrentTokenId()) {
+        case JsonTokenId.ID_STRING:
             String text = p.getText().trim();
-            if (text.length() == 0 || _hasTextualNull(text)) {
+            if (_isEmptyOrTextualNull(text)) {
+                _verifyNullForPrimitiveCoercion(ctxt, text);
                 return 0.0f;
             }
-            switch (text.charAt(0)) {
-            case 'I':
-                if (_isPosInf(text)) {
-                    return Float.POSITIVE_INFINITY;
-                }
-                break;
-            case 'N':
-                if (_isNaN(text)) { return Float.NaN; }
-                break;
-            case '-':
-                if (_isNegInf(text)) {
-                    return Float.NEGATIVE_INFINITY;
-                }
-                break;
-            }
-            try {
-                return Float.parseFloat(text);
-            } catch (IllegalArgumentException iae) { }
-            Number v = (Number) ctxt.handleWeirdStringValue(_valueClass, text,
-                    "not a valid float value");
-            return (v == null) ? 0 : v.floatValue();
-        }
-        if (t == JsonToken.VALUE_NULL) {
+            return _parseFloatPrimitive(ctxt, text);
+        case JsonTokenId.ID_NUMBER_INT:
+            return p.getFloatValue();
+        case JsonTokenId.ID_NULL:
+            _verifyNullForPrimitive(ctxt);
             return 0.0f;
-        }
-        if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
-            p.nextToken();
-            final float parsed = _parseFloatPrimitive(p, ctxt);
-            t = p.nextToken();
-            if (t != JsonToken.END_ARRAY) {
-                handleMissingEndArrayForSingle(p, ctxt);
-            }            
-            return parsed;            
+        case JsonTokenId.ID_START_ARRAY:
+            if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
+                p.nextToken();
+                final float parsed = _parseFloatPrimitive(p, ctxt);
+                _verifyEndArrayForSingle(p, ctxt);
+                return parsed;            
+            }
+            break;
         }
         // Otherwise, no can do:
         return ((Number) ctxt.handleUnexpectedToken(_valueClass, p)).floatValue();
     }
 
-    protected final Double _parseDouble(JsonParser p, DeserializationContext ctxt)
+    /**
+     * @since 2.9
+     */
+    protected final float _parseFloatPrimitive(DeserializationContext ctxt, String text)
         throws IOException
     {
-        JsonToken t = p.getCurrentToken();
-        
-        if (t == JsonToken.VALUE_NUMBER_INT || t == JsonToken.VALUE_NUMBER_FLOAT) { // coercing should work too
-            return p.getDoubleValue();
-        }
-        if (t == JsonToken.VALUE_STRING) {
-            String text = p.getText().trim();
-            if (text.length() == 0) {
-                return (Double) getEmptyValue(ctxt);
+        switch (text.charAt(0)) {
+        case 'I':
+            if (_isPosInf(text)) {
+                return Float.POSITIVE_INFINITY;
             }
-            if (_hasTextualNull(text)) {
-                return (Double) getNullValue(ctxt);
+            break;
+        case 'N':
+            if (_isNaN(text)) { return Float.NaN; }
+            break;
+        case '-':
+            if (_isNegInf(text)) {
+                return Float.NEGATIVE_INFINITY;
             }
-            switch (text.charAt(0)) {
-            case 'I':
-                if (_isPosInf(text)) {
-                    return Double.POSITIVE_INFINITY;
-                }
-                break;
-            case 'N':
-                if (_isNaN(text)) {
-                    return Double.NaN;
-                }
-                break;
-            case '-':
-                if (_isNegInf(text)) {
-                    return Double.NEGATIVE_INFINITY;
-                }
-                break;
-            }
-            try {
-                return parseDouble(text);
-            } catch (IllegalArgumentException iae) { }
-            return (Double) ctxt.handleWeirdStringValue(_valueClass, text,
-                    "not a valid Double value");
+            break;
         }
-        if (t == JsonToken.VALUE_NULL) {
-            return (Double) getNullValue(ctxt);
-        }
-        if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
-            p.nextToken();
-            final Double parsed = _parseDouble(p, ctxt);
-            t = p.nextToken();
-            if (t != JsonToken.END_ARRAY) {
-                handleMissingEndArrayForSingle(p, ctxt);
-            }            
-            return parsed;            
-        }
-        // Otherwise, no can do:
-        return (Double) ctxt.handleUnexpectedToken(_valueClass, p);
+        try {
+            return Float.parseFloat(text);
+        } catch (IllegalArgumentException iae) { }
+        Number v = (Number) ctxt.handleWeirdStringValue(_valueClass, text,
+                "not a valid float value");
+        return _nonNullNumber(v).floatValue();
     }
 
     protected final double _parseDoublePrimitive(JsonParser p, DeserializationContext ctxt)
         throws IOException
     {
-        // We accept couple of different types; obvious ones first:
-        JsonToken t = p.getCurrentToken();
-        
-        if (t == JsonToken.VALUE_NUMBER_INT || t == JsonToken.VALUE_NUMBER_FLOAT) { // coercing should work too
+        if (p.hasToken(JsonToken.VALUE_NUMBER_FLOAT)) {
             return p.getDoubleValue();
         }
-        // And finally, let's allow Strings to be converted too
-        if (t == JsonToken.VALUE_STRING) {
+        switch (p.getCurrentTokenId()) {
+        case JsonTokenId.ID_STRING:
             String text = p.getText().trim();
-            if (text.length() == 0 || _hasTextualNull(text)) {
+            if (_isEmptyOrTextualNull(text)) {
+                _verifyNullForPrimitiveCoercion(ctxt, text);
                 return 0.0;
             }
-            switch (text.charAt(0)) {
-            case 'I':
-                if (_isPosInf(text)) {
-                    return Double.POSITIVE_INFINITY;
-                }
-                break;
-            case 'N':
-                if (_isNaN(text)) {
-                    return Double.NaN;
-                }
-                break;
-            case '-':
-                if (_isNegInf(text)) {
-                    return Double.NEGATIVE_INFINITY;
-                }
-                break;
-            }
-            try {
-                return parseDouble(text);
-            } catch (IllegalArgumentException iae) { }
-            Number v = (Number) ctxt.handleWeirdStringValue(_valueClass, text, 
-                    "not a valid double value");
-            return (v == null) ? 0 : v.doubleValue();
-        }
-        if (t == JsonToken.VALUE_NULL) {
+            return _parseDoublePrimitive(ctxt, text);
+        case JsonTokenId.ID_NUMBER_INT:
+            return p.getDoubleValue();
+        case JsonTokenId.ID_NULL:
+            _verifyNullForPrimitive(ctxt);
             return 0.0;
-        }
-        // [databind#381]
-        if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
-            p.nextToken();
-            final double parsed = _parseDoublePrimitive(p, ctxt);
-            t = p.nextToken();
-            if (t != JsonToken.END_ARRAY) {
-                handleMissingEndArrayForSingle(p, ctxt);
-            }            
-            return parsed;            
+        case JsonTokenId.ID_START_ARRAY:
+            if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
+                p.nextToken();
+                final double parsed = _parseDoublePrimitive(p, ctxt);
+                _verifyEndArrayForSingle(p, ctxt);
+                return parsed;            
+            }
+            break;
         }
         // Otherwise, no can do:
         return ((Number) ctxt.handleUnexpectedToken(_valueClass, p)).doubleValue();
     }
 
+    /**
+     * @since 2.9
+     */
+    protected final double _parseDoublePrimitive(DeserializationContext ctxt, String text)
+        throws IOException
+    {
+        switch (text.charAt(0)) {
+        case 'I':
+            if (_isPosInf(text)) {
+                return Double.POSITIVE_INFINITY;
+            }
+            break;
+        case 'N':
+            if (_isNaN(text)) {
+                return Double.NaN;
+            }
+            break;
+        case '-':
+            if (_isNegInf(text)) {
+                return Double.NEGATIVE_INFINITY;
+            }
+            break;
+        }
+        try {
+            return parseDouble(text);
+        } catch (IllegalArgumentException iae) { }
+        Number v = (Number) ctxt.handleWeirdStringValue(_valueClass, text, 
+                "not a valid double value (as String to convert)");
+        return _nonNullNumber(v).doubleValue();
+    }
+
     protected java.util.Date _parseDate(JsonParser p, DeserializationContext ctxt)
         throws IOException
     {
-        JsonToken t = p.getCurrentToken();
-        if (t == JsonToken.VALUE_NUMBER_INT) {
-            return new java.util.Date(p.getLongValue());
-        }
-        if (t == JsonToken.VALUE_NULL) {
-            return (java.util.Date) getNullValue(ctxt);
-        }
-        if (t == JsonToken.VALUE_STRING) {
+        switch (p.getCurrentTokenId()) {
+        case JsonTokenId.ID_STRING:
             return _parseDate(p.getText().trim(), ctxt);
+        case JsonTokenId.ID_NUMBER_INT:
+            {
+                long ts;
+                try {
+                    ts = p.getLongValue();
+                } catch (JsonParseException e) {
+                    Number v = (Number) ctxt.handleWeirdNumberValue(_valueClass, p.getNumberValue(),
+                            "not a valid 64-bit long for creating `java.util.Date`");
+                    ts = v.longValue();
+                }
+                return new java.util.Date(ts);
+            }
+        case JsonTokenId.ID_NULL:
+            return (java.util.Date) getNullValue(ctxt);
+        case JsonTokenId.ID_START_ARRAY:
+            return _parseDateFromArray(p, ctxt);
         }
-        // [databind#381]
-        if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
-            p.nextToken();
-            final Date parsed = _parseDate(p, ctxt);
+        return (java.util.Date) ctxt.handleUnexpectedToken(_valueClass, p);
+    }
+
+    // @since 2.9
+    protected java.util.Date _parseDateFromArray(JsonParser p, DeserializationContext ctxt)
+            throws IOException
+    {
+        JsonToken t;
+        if (ctxt.hasSomeOfFeatures(F_MASK_ACCEPT_ARRAYS)) {
             t = p.nextToken();
-            if (t != JsonToken.END_ARRAY) {
-                handleMissingEndArrayForSingle(p, ctxt);
-            }            
-            return parsed;            
+            if (t == JsonToken.END_ARRAY) {
+                if (ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT)) {
+                    return (java.util.Date) getNullValue(ctxt);
+                }
+            }
+            if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
+                final Date parsed = _parseDate(p, ctxt);
+                _verifyEndArrayForSingle(p, ctxt);
+                return parsed;            
+            }
+        } else {
+            t = p.getCurrentToken();
         }
-        return (java.util.Date)  ctxt.handleUnexpectedToken(_valueClass, p);
+        return (java.util.Date) ctxt.handleUnexpectedToken(_valueClass, t, p, null);
     }
 
     /**
@@ -808,10 +516,7 @@
     {
         try {
             // Take empty Strings to mean 'empty' Value, usually 'null':
-            if (value.length() == 0) {
-                return (Date) getEmptyValue(ctxt);
-            }
-            if (_hasTextualNull(value)) {
+            if (_isEmptyOrTextualNull(value)) {
                 return (java.util.Date) getNullValue(ctxt);
             }
             return ctxt.parseDate(value);
@@ -846,15 +551,17 @@
         if (t == JsonToken.VALUE_STRING) {
             return p.getText();
         }
+        // 07-Nov-2016, tatu: Caller should take care of unwrapping and there shouldn't
+        //    be need for extra pass here...
+        /*
         // [databind#381]
         if ((t == JsonToken.START_ARRAY) && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
             p.nextToken();
             final String parsed = _parseString(p, ctxt);
-            if (p.nextToken() != JsonToken.END_ARRAY) {
-                handleMissingEndArrayForSingle(p, ctxt);
-            }            
+            _verifyEndArrayForSingle(p, ctxt);
             return parsed;            
         }
+        */
         String value = p.getValueAsString();
         if (value != null) {
             return value;
@@ -903,6 +610,13 @@
         return "null".equals(value);
     }
 
+    /**
+     * @since 2.9
+     */
+    protected boolean _isEmptyOrTextualNull(String value) {
+        return value.isEmpty() || "null".equals(value);
+    }
+    
     protected final boolean _isNegInf(String text) {
         return "-Infinity".equals(text) || "-INF".equals(text);
     }
@@ -914,11 +628,91 @@
     protected final boolean _isNaN(String text) { return "NaN".equals(text); }
 
     /*
+    /**********************************************************
+    /* Helper methods for sub-classes regarding decoding from
+    /* alternate representations
+    /**********************************************************
+     */
+
+    /**
+     * Helper method that allows easy support for array-related {@link DeserializationFeature}s
+     * `ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT` and `UNWRAP_SINGLE_VALUE_ARRAYS`: checks for either
+     * empty array, or single-value array-wrapped value (respectively), and either reports
+     * an exception (if no match, or feature(s) not enabled), or returns appropriate
+     * result value.
+     *<p>
+     * This method should NOT be called if Array representation is explicitly supported
+     * for type: it should only be called in case it is otherwise unrecognized.
+     *<p>
+     * NOTE: in case of unwrapped single element, will handle actual decoding
+     * by calling {@link #_deserializeWrappedValue}, which by default calls
+     * {@link #deserialize(JsonParser, DeserializationContext)}.
+     *
+     * @since 2.9
+     */
+    protected T _deserializeFromArray(JsonParser p, DeserializationContext ctxt) throws IOException
+    {
+        JsonToken t;
+        if (ctxt.hasSomeOfFeatures(F_MASK_ACCEPT_ARRAYS)) {
+            t = p.nextToken();
+            if (t == JsonToken.END_ARRAY) {
+                if (ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT)) {
+                    return getNullValue(ctxt);
+                }
+            }
+            if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
+                final T parsed = deserialize(p, ctxt);
+                if (p.nextToken() != JsonToken.END_ARRAY) {
+                    handleMissingEndArrayForSingle(p, ctxt);
+                }
+                return parsed;            
+            }
+        } else {
+            t = p.getCurrentToken();
+        }
+        @SuppressWarnings("unchecked")
+        T result = (T) ctxt.handleUnexpectedToken(_valueClass, t, p, null);
+        return result;
+    }
+
+    /**
+     * Helper called to support {@link DeserializationFeature#UNWRAP_SINGLE_VALUE_ARRAYS}:
+     * default implementation simply calls
+     * {@link #deserialize(JsonParser, DeserializationContext)},
+     * but handling may be overridden.
+     *
+     * @since 2.9
+     */
+    protected T _deserializeWrappedValue(JsonParser p, DeserializationContext ctxt) throws IOException
+    {
+        // 23-Mar-2017, tatu: Let's specifically block recursive resolution to avoid
+        //   either supporting nested arrays, or to cause infinite looping.
+        if (p.hasToken(JsonToken.START_ARRAY)) {
+            String msg = String.format(
+"Cannot deserialize instance of %s out of %s token: nested Arrays not allowed with %s",
+                    ClassUtil.nameOf(_valueClass), JsonToken.START_ARRAY,
+                    "DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS");
+            @SuppressWarnings("unchecked")
+            T result = (T) ctxt.handleUnexpectedToken(_valueClass, p.getCurrentToken(), p, msg);
+            return result;
+        }
+        return (T) deserialize(p, ctxt);
+    }
+
+    /*
     /****************************************************
     /* Helper methods for sub-classes, coercions
     /****************************************************
      */
 
+    protected void _failDoubleToIntCoercion(JsonParser p, DeserializationContext ctxt,
+            String type) throws IOException
+    {
+        ctxt.reportInputMismatch(handledType(),
+"Cannot coerce a floating-point value ('%s') into %s (enable `DeserializationFeature.ACCEPT_FLOAT_AS_INT` to allow)",
+                p.getValueAsString(), type);
+    }
+
     /**
      * Helper method called in case where an integral number is encountered, but
      * config settings suggest that a coercion may be needed to "upgrade"
@@ -941,7 +735,168 @@
         }
         return p.getBigIntegerValue(); // should be optimal, whatever it is
     }
+
+    /**
+     * Method to call when JSON `null` token is encountered. Note: only called when
+     * this deserializer encounters it but NOT when reached via property
+     *
+     * @since 2.9
+     */
+    protected Object _coerceNullToken(DeserializationContext ctxt, boolean isPrimitive) throws JsonMappingException
+    {
+        if (isPrimitive) {
+            _verifyNullForPrimitive(ctxt);
+        }
+        return getNullValue(ctxt);
+    }
+
+    /**
+     * Method called when JSON String with value "null" is encountered.
+     *
+     * @since 2.9
+     */
+    protected Object _coerceTextualNull(DeserializationContext ctxt, boolean isPrimitive) throws JsonMappingException
+    {
+        Enum<?> feat;
+        boolean enable;
+
+        if (!ctxt.isEnabled(MapperFeature.ALLOW_COERCION_OF_SCALARS)) {
+            feat = MapperFeature.ALLOW_COERCION_OF_SCALARS;
+            enable = true;
+        } else if (isPrimitive && ctxt.isEnabled(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)) {
+            feat = DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES;
+            enable = false;
+        } else {
+            return getNullValue(ctxt);
+        }
+        _reportFailedNullCoerce(ctxt, enable, feat, "String \"null\"");
+        return null;
+    }
+
+    /**
+     * Method called when JSON String with value "" (that is, zero length) is encountered.
+     *
+     * @since 2.9
+     */
+    protected Object _coerceEmptyString(DeserializationContext ctxt, boolean isPrimitive) throws JsonMappingException
+    {
+        Enum<?> feat;
+        boolean enable;
+
+        if (!ctxt.isEnabled(MapperFeature.ALLOW_COERCION_OF_SCALARS)) {
+            feat = MapperFeature.ALLOW_COERCION_OF_SCALARS;
+            enable = true;
+        } else if (isPrimitive && ctxt.isEnabled(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)) {
+            feat = DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES;
+            enable = false;
+        } else {
+            return getNullValue(ctxt);
+        }
+        _reportFailedNullCoerce(ctxt, enable, feat, "empty String (\"\")");
+        return null;
+    }
+
+    // @since 2.9
+    protected final void _verifyNullForPrimitive(DeserializationContext ctxt) throws JsonMappingException
+    {
+        if (ctxt.isEnabled(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)) {
+            ctxt.reportInputMismatch(this,
+"Cannot coerce `null` %s (disable `DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES` to allow)",
+                    _coercedTypeDesc());
+        }
+    }
+
+    // NOTE: only for primitive Scalars
+    // @since 2.9
+    protected final void _verifyNullForPrimitiveCoercion(DeserializationContext ctxt, String str) throws JsonMappingException
+    {
+        Enum<?> feat;
+        boolean enable;
+
+        if (!ctxt.isEnabled(MapperFeature.ALLOW_COERCION_OF_SCALARS)) {
+            feat = MapperFeature.ALLOW_COERCION_OF_SCALARS;
+            enable = true;
+        } else if (ctxt.isEnabled(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)) {
+            feat = DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES;
+            enable = false;
+        } else {
+            return;
+        }
+        String strDesc = str.isEmpty() ? "empty String (\"\")" : String.format("String \"%s\"", str);
+        _reportFailedNullCoerce(ctxt, enable, feat, strDesc);
+    }
+
+    // NOTE: for non-primitive Scalars
+    // @since 2.9
+    protected final void _verifyNullForScalarCoercion(DeserializationContext ctxt, String str) throws JsonMappingException
+    {
+        if (!ctxt.isEnabled(MapperFeature.ALLOW_COERCION_OF_SCALARS)) {
+            String strDesc = str.isEmpty() ? "empty String (\"\")" : String.format("String \"%s\"", str);
+            _reportFailedNullCoerce(ctxt, true, MapperFeature.ALLOW_COERCION_OF_SCALARS, strDesc);
+        }
+    }
+
+    // @since 2.9
+    protected void _verifyStringForScalarCoercion(DeserializationContext ctxt, String str) throws JsonMappingException
+    {
+        MapperFeature feat = MapperFeature.ALLOW_COERCION_OF_SCALARS;
+        if (!ctxt.isEnabled(feat)) {
+            ctxt.reportInputMismatch(this, "Cannot coerce String \"%s\" %s (enable `%s.%s` to allow)",
+                str, _coercedTypeDesc(), feat.getClass().getSimpleName(), feat.name());
+        }
+    }
+
+    // @since 2.9
+    protected void _verifyNumberForScalarCoercion(DeserializationContext ctxt, JsonParser p) throws IOException
+    {
+        MapperFeature feat = MapperFeature.ALLOW_COERCION_OF_SCALARS;
+        if (!ctxt.isEnabled(feat)) {
+            // 31-Mar-2017, tatu: Since we don't know (or this deep, care) about exact type,
+            //   access as a String: may require re-encoding by parser which should be fine
+            String valueDesc = p.getText();
+            ctxt.reportInputMismatch(this, "Cannot coerce Number (%s) %s (enable `%s.%s` to allow)",
+                valueDesc, _coercedTypeDesc(), feat.getClass().getSimpleName(), feat.name());
+        }
+    }
     
+    protected void _reportFailedNullCoerce(DeserializationContext ctxt, boolean state, Enum<?> feature,
+            String inputDesc) throws JsonMappingException
+    {
+        String enableDesc = state ? "enable" : "disable";
+        ctxt.reportInputMismatch(this, "Cannot coerce %s to Null value %s (%s `%s.%s` to allow)",
+            inputDesc, _coercedTypeDesc(), enableDesc, feature.getClass().getSimpleName(), feature.name());
+    }
+    
+    /**
+     * Helper method called to get a description of type into which a scalar value coercion
+     * is (most likely) being applied, to be used for constructing exception messages
+     * on coerce failure.
+     *
+     * @return Message with backtick-enclosed name of type this deserializer supports
+     *
+     * @since 2.9
+     */
+    protected String _coercedTypeDesc() {
+        boolean structured;
+        String typeDesc;
+
+        JavaType t = getValueType();
+        if ((t != null) && !t.isPrimitive()) {
+            structured = (t.isContainerType() || t.isReferenceType());
+            // 21-Jul-2017, tatu: Probably want to change this (JavaType.toString() not very good) but...
+            typeDesc = "'"+t.toString()+"'";
+        } else {
+            Class<?> cls = handledType();
+            structured = cls.isArray() || Collection.class.isAssignableFrom(cls)
+                || Map.class.isAssignableFrom(cls);
+            typeDesc = ClassUtil.nameOf(cls);
+        }
+        if (structured) {
+            return "as content of type "+typeDesc;
+        }
+        return "for type "+typeDesc;
+    }
+
     /*
     /****************************************************
     /* Helper methods for sub-classes, resolving dependencies
@@ -988,10 +943,10 @@
 
     /*
     /**********************************************************
-    /* Helper methods for sub-classes, deserializer construction
+    /* Helper methods for: deserializer construction
     /**********************************************************
      */
-    
+
     /**
      * Helper method that can be used to see if specified property has annotation
      * indicating that a converter is to be used for contained values (contents
@@ -1007,7 +962,7 @@
         throws JsonMappingException
     {
         final AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
-        if (intr != null && prop != null) {
+        if (_neitherNull(intr, prop)) {
             AnnotatedMember member = prop.getMember();
             if (member != null) {
                 Object convDef = intr.findDeserializationContentConverter(member);
@@ -1024,6 +979,12 @@
         return existingDeserializer;
     }
 
+    /*
+    /**********************************************************
+    /* Helper methods for: accessing contextual config settings
+    /**********************************************************
+     */
+    
     /**
      * Helper method that may be used to find if this deserializer has specific
      * {@link JsonFormat} settings, either via property, or through type-specific
@@ -1063,6 +1024,103 @@
         return null;
     }
 
+    /**
+     * Method called to find {@link NullValueProvider} for a primary property, using
+     * "value nulls" setting. If no provider found (not defined, or is "skip"),
+     * will return `null`.
+     *
+     * @since 2.9
+     */
+    protected final NullValueProvider findValueNullProvider(DeserializationContext ctxt,
+            SettableBeanProperty prop, PropertyMetadata propMetadata)
+        throws JsonMappingException
+    {
+        if (prop != null) {
+            return _findNullProvider(ctxt, prop, propMetadata.getValueNulls(),
+                    prop.getValueDeserializer());
+        }
+        return null;
+    }
+
+    /**
+     * Method called to find {@link NullValueProvider} for a contents of a structured
+     * primary property (Collection, Map, array), using
+     * "content nulls" setting. If no provider found (not defined),
+     * will return given value deserializer (which is a null value provider itself).
+     *
+     * @since 2.9
+     */
+    protected NullValueProvider findContentNullProvider(DeserializationContext ctxt,
+            BeanProperty prop, JsonDeserializer<?> valueDeser)
+        throws JsonMappingException
+    {
+        final Nulls nulls = findContentNullStyle(ctxt, prop);
+        if (nulls == Nulls.SKIP) {
+            return NullsConstantProvider.skipper();
+        }
+        NullValueProvider prov = _findNullProvider(ctxt, prop, nulls, valueDeser);
+        if (prov != null) {
+            return prov;
+        }
+        return valueDeser;
+    }
+
+    protected Nulls findContentNullStyle(DeserializationContext ctxt, BeanProperty prop)
+        throws JsonMappingException
+    {
+        if (prop != null) {
+            return prop.getMetadata().getContentNulls();
+        }
+        return null;
+    }
+
+    // @since 2.9
+    protected final NullValueProvider _findNullProvider(DeserializationContext ctxt,
+            BeanProperty prop, Nulls nulls, JsonDeserializer<?> valueDeser)
+        throws JsonMappingException
+    {
+        if (nulls == Nulls.FAIL) {
+            if (prop == null) {
+                return NullsFailProvider.constructForRootValue(ctxt.constructType(valueDeser.handledType()));
+            }
+            return NullsFailProvider.constructForProperty(prop);
+        }
+        if (nulls == Nulls.AS_EMPTY) {
+            // cannot deal with empty values if there is no value deserializer that
+            // can indicate what "empty value" is:
+            if (valueDeser == null) {
+                return null;
+            }
+
+            // Let's first do some sanity checking...
+            // NOTE: although we could use `ValueInstantiator.Gettable` in general,
+            // let's not since that would prevent being able to use custom impls:
+            if (valueDeser instanceof BeanDeserializerBase) {
+                ValueInstantiator vi = ((BeanDeserializerBase) valueDeser).getValueInstantiator();
+                if (!vi.canCreateUsingDefault()) {
+                    final JavaType type = prop.getType();
+                    ctxt.reportBadDefinition(type,
+                            String.format("Cannot create empty instance of %s, no default Creator", type));
+                }
+            }
+            // Second: can with pre-fetch value?
+            {
+                AccessPattern access = valueDeser.getEmptyAccessPattern();
+                if (access == AccessPattern.ALWAYS_NULL) {
+                    return NullsConstantProvider.nuller();
+                }
+                if (access == AccessPattern.CONSTANT) {
+                    return NullsConstantProvider.forValue(valueDeser.getEmptyValue(ctxt));
+                }
+            }
+            return new NullsAsEmptyProvider(valueDeser);
+        }
+        if (nulls == Nulls.SKIP) {
+            return NullsConstantProvider.skipper();
+        }
+        return null;
+    }
+
     /*
     /**********************************************************
     /* Helper methods for sub-classes, problem reporting
@@ -1081,9 +1139,10 @@
      * @param instanceOrClass Instance that is being populated by this
      *   deserializer, or if not known, Class that would be instantiated.
      *   If null, will assume type is what {@link #getValueClass} returns.
-     * @param propName Name of the property that can not be mapped
+     * @param propName Name of the property that cannot be mapped
      */
-    protected void handleUnknownProperty(JsonParser p, DeserializationContext ctxt, Object instanceOrClass, String propName)
+    protected void handleUnknownProperty(JsonParser p, DeserializationContext ctxt,
+            Object instanceOrClass, String propName)
         throws IOException
     {
         if (instanceOrClass == null) {
@@ -1102,17 +1161,64 @@
     protected void handleMissingEndArrayForSingle(JsonParser p, DeserializationContext ctxt)
         throws IOException
     {
-        ctxt.reportWrongTokenException(p, JsonToken.END_ARRAY, 
-"Attempted to unwrap single value array for single '%s' value but there was more than a single value in the array",
+        ctxt.reportWrongTokenException(this, JsonToken.END_ARRAY, 
+"Attempted to unwrap '%s' value from an array (with `DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS`) but it contains more than one value",
 handledType().getName());
         // 05-May-2016, tatu: Should recover somehow (maybe skip until END_ARRAY);
         //     but for now just fall through
     }
 
-    protected void _failDoubleToIntCoercion(JsonParser p, DeserializationContext ctxt,
-            String type) throws IOException
+    protected void _verifyEndArrayForSingle(JsonParser p, DeserializationContext ctxt) throws IOException
     {
-        ctxt.reportMappingException("Can not coerce a floating-point value ('%s') into %s; enable `DeserializationFeature.ACCEPT_FLOAT_AS_INT` to allow",
-                        p.getValueAsString(), type);
+        JsonToken t = p.nextToken();
+        if (t != JsonToken.END_ARRAY) {
+            handleMissingEndArrayForSingle(p, ctxt);
+        }            
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods, other
+    /**********************************************************
+     */
+
+    /**
+     * @since 2.9
+     */
+    protected final static boolean _neitherNull(Object a, Object b) {
+        return (a != null) && (b != null);
+    }
+
+    /**
+     * @since 2.9
+     */
+    protected final boolean _byteOverflow(int value) {
+        // 07-nov-2016, tatu: We support "unsigned byte" as well
+        //    as Java signed range since that's relatively common usage
+        return (value < Byte.MIN_VALUE || value > 255);
+    }
+    
+    /**
+     * @since 2.9
+     */
+    protected final boolean _shortOverflow(int value) {
+        return (value < Short.MIN_VALUE || value > Short.MAX_VALUE);
+    }
+
+    /**
+     * @since 2.9
+     */
+    protected final boolean _intOverflow(long value) {
+        return (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE);
+    }
+
+    /**
+     * @since 2.9
+     */
+    protected Number _nonNullNumber(Number n) {
+        if (n == null) {
+            n = Integer.valueOf(0);
+        }
+        return n;
     }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdKeyDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdKeyDeserializer.java
index de6b23b..29a944b 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdKeyDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdKeyDeserializer.java
@@ -47,6 +47,7 @@
     public final static int TYPE_URL = 14;
     public final static int TYPE_CLASS = 15;
     public final static int TYPE_CURRENCY = 16;
+    public final static int TYPE_BYTE_ARRAY = 17; // since 2.9
 
     final protected int _kind;
     final protected Class<?> _keyClass;
@@ -108,6 +109,8 @@
         } else if (raw == Currency.class) {
             FromStringDeserializer<?> deser = FromStringDeserializer.findDeserializer(Currency.class);
             return new StdKeyDeserializer(TYPE_CURRENCY, raw, deser);
+        } else if (raw == byte[].class) {
+            kind = TYPE_BYTE_ARRAY;
         } else {
             return null;
         }
@@ -154,7 +157,7 @@
                 int value = _parseInt(key);
                 // allow range up to 255, inclusive (to support "unsigned" byte)
                 if (value < Byte.MIN_VALUE || value > 255) {
-                    return ctxt.handleWeirdKey(_keyClass, key, "overflow, value can not be represented as 8-bit value");
+                    return ctxt.handleWeirdKey(_keyClass, key, "overflow, value cannot be represented as 8-bit value");
                 }
                 return Byte.valueOf((byte) value);
             }
@@ -162,7 +165,7 @@
             {
                 int value = _parseInt(key);
                 if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) {
-                    return ctxt.handleWeirdKey(_keyClass, key, "overflow, value can not be represented as 16-bit value");
+                    return ctxt.handleWeirdKey(_keyClass, key, "overflow, value cannot be represented as 16-bit value");
                     // fall-through and truncate if need be
                 }
                 return Short.valueOf((short) value);
@@ -186,37 +189,36 @@
         case TYPE_LOCALE:
             try {
                 return _deser._deserialize(key, ctxt);
-            } catch (IOException e) {
-                return ctxt.handleWeirdKey(_keyClass, key, "unable to parse key as locale");
+            } catch (IllegalArgumentException e) {
+                return _weirdKey(ctxt, key, e);
             }
         case TYPE_CURRENCY:
             try {
                 return _deser._deserialize(key, ctxt);
-            } catch (IOException e) {
-                return ctxt.handleWeirdKey(_keyClass, key, "unable to parse key as currency");
+            } catch (IllegalArgumentException e) {
+                return _weirdKey(ctxt, key, e);
             }
         case TYPE_DATE:
             return ctxt.parseDate(key);
         case TYPE_CALENDAR:
-            java.util.Date date = ctxt.parseDate(key);
-            return (date == null)  ? null : ctxt.constructCalendar(date);
+            return ctxt.constructCalendar(ctxt.parseDate(key));
         case TYPE_UUID:
             try {
                 return UUID.fromString(key);
             } catch (Exception e) {
-                return ctxt.handleWeirdKey(_keyClass, key, "problem: %s", e.getMessage());
+                return _weirdKey(ctxt, key, e);
             }
         case TYPE_URI:
             try {
                 return URI.create(key);
             } catch (Exception e) {
-                return ctxt.handleWeirdKey(_keyClass, key, "problem: %s", e.getMessage());
+                return _weirdKey(ctxt, key, e);
             }
         case TYPE_URL:
             try {
                 return new URL(key);
             } catch (MalformedURLException e) {
-                return ctxt.handleWeirdKey(_keyClass, key, "problem: %s", e.getMessage());
+                return _weirdKey(ctxt, key, e);
             }
         case TYPE_CLASS:
             try {
@@ -224,6 +226,12 @@
             } catch (Exception e) {
                 return ctxt.handleWeirdKey(_keyClass, key, "unable to parse key as Class");
             }
+        case TYPE_BYTE_ARRAY:
+            try {
+                return ctxt.getConfig().getBase64Variant().decode(key);
+            } catch (IllegalArgumentException e) {
+                return _weirdKey(ctxt, key, e);
+            }
         default:
             throw new IllegalStateException("Internal error: unknown key type "+_keyClass);
         }
@@ -247,6 +255,11 @@
         return NumberInput.parseDouble(key);
     }
 
+    // @since 2.9
+    protected Object _weirdKey(DeserializationContext ctxt, String key, Exception e) throws IOException {
+        return ctxt.handleWeirdKey(_keyClass, key, "problem: %s", e.getMessage());
+    }
+
     /*
     /**********************************************************
     /* First: the standard "String as String" deserializer
@@ -348,11 +361,14 @@
          * @since 2.7.3
          */
         protected EnumResolver _byToStringResolver;
+
+        protected final Enum<?> _enumDefaultValue;
         
         protected EnumKD(EnumResolver er, AnnotatedMethod factory) {
             super(-1, er.getEnumClass());
             _byNameResolver = er;
             _factory = factory;
+            _enumDefaultValue = er.getDefaultValue();
         }
 
         @Override
@@ -368,9 +384,14 @@
             EnumResolver res = ctxt.isEnabled(DeserializationFeature.READ_ENUMS_USING_TO_STRING)
                     ? _getToStringResolver(ctxt) : _byNameResolver;
             Enum<?> e = res.findEnum(key);
-            if ((e == null) && !ctxt.getConfig().isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
-                return ctxt.handleWeirdKey(_keyClass, key, "not one of values excepted for Enum class: %s",
+            if (e == null) {
+                if ((_enumDefaultValue != null)
+                        && ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE)) {
+                    e = _enumDefaultValue;
+                } else if (!ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
+                    return ctxt.handleWeirdKey(_keyClass, key, "not one of values excepted for Enum class: %s",
                         res.getEnumIds());
+                }
                 // fall-through if problems are collected, not immediately thrown
             }
             return e;
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdScalarDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdScalarDeserializer.java
index f75d101..1e719e4 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdScalarDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdScalarDeserializer.java
@@ -3,10 +3,9 @@
 import java.io.IOException;
 
 import com.fasterxml.jackson.core.*;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.DeserializationFeature;
-import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.util.AccessPattern;
 
 /**
  * Base class for deserializers that handle types that are serialized
@@ -14,11 +13,6 @@
  */
 public abstract class StdScalarDeserializer<T> extends StdDeserializer<T>
 {
-    // @since 2.8.8
-    protected final static int FEATURES_ACCEPT_ARRAYS =
-            DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS.getMask() |
-            DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT.getMask();
-
     private static final long serialVersionUID = 1L;
 
     protected StdScalarDeserializer(Class<?> vc) { super(vc); }
@@ -32,28 +26,37 @@
         return typeDeserializer.deserializeTypedFromScalar(p, ctxt);
     }
 
-    protected T _deserializeFromArray(JsonParser p, DeserializationContext ctxt) throws IOException
-    {
-        JsonToken t;
-        if (ctxt.hasSomeOfFeatures(FEATURES_ACCEPT_ARRAYS)) {
-            t = p.nextToken();
-            if (t == JsonToken.END_ARRAY) {
-                if (ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT)) {
-                    return getNullValue(ctxt);
-                }
-            }
-            if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
-                final T parsed = deserialize(p, ctxt);
-                if (p.nextToken() != JsonToken.END_ARRAY) {
-                    handleMissingEndArrayForSingle(p, ctxt);
-                }
-                return parsed;            
-            }
-        } else {
-            t = p.getCurrentToken();
-        }
-        @SuppressWarnings("unchecked")
-        T result = (T) ctxt.handleUnexpectedToken(_valueClass, t, p, null);
-        return result;
+    /**
+     * Overridden to simply call <code>deserialize()</code> method that does not take value
+     * to update, since scalar values are usually non-mergeable.
+     */
+    @Override // since 2.9
+    public T deserialize(JsonParser p, DeserializationContext ctxt, T intoValue) throws IOException {
+        // 25-Oct-2016, tatu: And if attempt is made, see if we are to complain...
+        ctxt.reportBadMerge(this);
+        // except that it is possible to suppress this; and if so...
+        return deserialize(p, ctxt);
+    }
+
+    /**
+     * By default assumption is that scalar types cannot be updated: many are immutable
+     * values (such as primitives and wrappers)
+     */
+    @Override // since 2.9
+    public Boolean supportsUpdate(DeserializationConfig config) {
+        return Boolean.FALSE;
+    }
+
+    // Typically Scalar values have default setting of "nulls as nulls"
+    @Override
+    public AccessPattern getNullAccessPattern() {
+        return AccessPattern.ALWAYS_NULL;
+    }
+
+    // While some scalar types have non-null empty values (hence can't say "ALWAYS_NULL")
+    // they are mostly immutable, shareable and so constant.
+    @Override // since 2.9
+    public AccessPattern getEmptyAccessPattern() {
+        return AccessPattern.CONSTANT;
     }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdValueInstantiator.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdValueInstantiator.java
index 9705770..3d112e4 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdValueInstantiator.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdValueInstantiator.java
@@ -8,6 +8,7 @@
 import com.fasterxml.jackson.databind.deser.*;
 import com.fasterxml.jackson.databind.introspect.AnnotatedParameter;
 import com.fasterxml.jackson.databind.introspect.AnnotatedWithParams;
+import com.fasterxml.jackson.databind.util.ClassUtil;
 
 /**
  * Default {@link ValueInstantiator} implementation, which supports
@@ -79,7 +80,7 @@
      */
     @Deprecated
     public StdValueInstantiator(DeserializationConfig config, Class<?> valueType) {
-        _valueTypeDesc = (valueType == null) ? "UNKNOWN TYPE" : valueType.getName();
+        _valueTypeDesc = ClassUtil.nameOf(valueType);
         _valueClass = (valueType == null) ? Object.class : valueType;
     }
 
@@ -267,9 +268,8 @@
         }
         try {
             return _defaultCreator.call();
-        } catch (Throwable t) {
-            return ctxt.handleInstantiationProblem(_defaultCreator.getDeclaringClass(),
-                    null, rewrapCtorProblem(ctxt, t));
+        } catch (Exception e) { // 19-Apr-2017, tatu: Let's not catch Errors, just Exceptions
+            return ctxt.handleInstantiationProblem(_valueClass, null, rewrapCtorProblem(ctxt, e));
         }
     }
 
@@ -281,9 +281,8 @@
         }
         try {
             return _withArgsCreator.call(args);
-        } catch (Throwable t) {
-            return ctxt.handleInstantiationProblem(_withArgsCreator.getDeclaringClass(),
-                    args, rewrapCtorProblem(ctxt, t));
+        } catch (Exception e) { // 19-Apr-2017, tatu: Let's not catch Errors, just Exceptions
+            return ctxt.handleInstantiationProblem(_valueClass, args, rewrapCtorProblem(ctxt, e));
         }
     }
 
@@ -510,8 +509,7 @@
     /**********************************************************
      */
 
-    private Object _createUsingDelegate(
-            AnnotatedWithParams delegateCreator,
+    private Object _createUsingDelegate(AnnotatedWithParams delegateCreator,
             SettableBeanProperty[] delegateArguments,
             DeserializationContext ctxt,
             Object delegate)
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/StringArrayDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StringArrayDeserializer.java
index 38f319f..a348a40 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/StringArrayDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StringArrayDeserializer.java
@@ -7,7 +7,10 @@
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
 import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
+import com.fasterxml.jackson.databind.deser.NullValueProvider;
+import com.fasterxml.jackson.databind.deser.impl.NullsConstantProvider;
 import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.util.AccessPattern;
 import com.fasterxml.jackson.databind.util.ObjectBuffer;
 
 /**
@@ -17,11 +20,16 @@
  */
 @JacksonStdImpl
 public final class StringArrayDeserializer
+// 28-Oct-2016, tatu: Should do this:
+//    extends ContainerDeserializerBase<String[]>
+// but for now won't:
     extends StdDeserializer<String[]>
     implements ContextualDeserializer
 {
     private static final long serialVersionUID = 2L;
 
+    private final static String[] NO_STRINGS = new String[0];
+
     public final static StringArrayDeserializer instance = new StringArrayDeserializer();
 
     /**
@@ -30,6 +38,13 @@
     protected JsonDeserializer<String> _elementDeserializer;
 
     /**
+     * Handler we need for dealing with nulls.
+     *
+     * @since 2.9
+     */
+    protected final NullValueProvider _nullProvider;
+
+    /**
      * Specific override for this instance (from proper, or global per-type overrides)
      * to indicate whether single value may be taken to mean an unwrapped one-element array
      * or not. If null, left to global defaults.
@@ -38,15 +53,42 @@
      */
     protected final Boolean _unwrapSingle;
 
+    /**
+     * Marker flag set if the <code>_nullProvider</code> indicates that all null
+     * content values should be skipped (instead of being possibly converted).
+     *
+     * @since 2.9
+     */
+    protected final boolean _skipNullValues;
+    
     public StringArrayDeserializer() {
-        this(null, null);
+        this(null, null, null);
     }
 
     @SuppressWarnings("unchecked")
-    protected StringArrayDeserializer(JsonDeserializer<?> deser, Boolean unwrapSingle) {
+    protected StringArrayDeserializer(JsonDeserializer<?> deser,
+            NullValueProvider nuller, Boolean unwrapSingle) {
         super(String[].class);
         _elementDeserializer = (JsonDeserializer<String>) deser;
+        _nullProvider = nuller;
         _unwrapSingle = unwrapSingle;
+        _skipNullValues = NullsConstantProvider.isSkipper(nuller);
+    }
+
+    @Override // since 2.9
+    public Boolean supportsUpdate(DeserializationConfig config) {
+        return Boolean.TRUE;
+    }
+
+    @Override // since 2.9
+    public AccessPattern getEmptyAccessPattern() {
+        // immutable, shareable so:
+        return AccessPattern.CONSTANT;
+    }
+
+    @Override // since 2.9
+    public Object getEmptyValue(DeserializationContext ctxt) throws JsonMappingException {
+        return NO_STRINGS;
     }
 
     /**
@@ -54,7 +96,8 @@
      * of String values, or if we have to use separate value deserializer.
      */
     @Override
-    public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException
+    public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)
+            throws JsonMappingException
     {
         JsonDeserializer<?> deser = _elementDeserializer;
         // May have a content converter
@@ -68,14 +111,17 @@
         // One more thing: allow unwrapping?
         Boolean unwrapSingle = findFormatFeature(ctxt, property, String[].class,
                 JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
+        NullValueProvider nuller = findContentNullProvider(ctxt, property, deser);
         // Ok ok: if all we got is the default String deserializer, can just forget about it
         if ((deser != null) && isDefaultDeserializer(deser)) {
             deser = null;
         }
-        if ((_elementDeserializer == deser) && (_unwrapSingle == unwrapSingle)) {
+        if ((_elementDeserializer == deser)
+                && (_unwrapSingle == unwrapSingle)
+                && (_nullProvider == nuller)) {
             return this;
         }
-        return new StringArrayDeserializer(deser, unwrapSingle);
+        return new StringArrayDeserializer(deser, nuller, unwrapSingle);
     }
 
     @Override
@@ -86,7 +132,7 @@
             return handleNonArray(p, ctxt);
         }
         if (_elementDeserializer != null) {
-            return _deserializeCustom(p, ctxt);
+            return _deserializeCustom(p, ctxt, null);
         }
 
         final ObjectBuffer buffer = ctxt.leaseObjectBuffer();
@@ -102,7 +148,12 @@
                     if (t == JsonToken.END_ARRAY) {
                         break;
                     }
-                    if (t != JsonToken.VALUE_NULL) {
+                    if (t == JsonToken.VALUE_NULL) {
+                        if (_skipNullValues) {
+                            continue;
+                        }
+                        value = (String) _nullProvider.getNullValue(ctxt);
+                    } else {
                         value = _parseString(p, ctxt);
                     }
                 }
@@ -123,13 +174,22 @@
     /**
      * Offlined version used when we do not use the default deserialization method.
      */
-    protected final String[] _deserializeCustom(JsonParser p, DeserializationContext ctxt) throws IOException
+    protected final String[] _deserializeCustom(JsonParser p, DeserializationContext ctxt,
+            String[] old) throws IOException
     {
         final ObjectBuffer buffer = ctxt.leaseObjectBuffer();
-        Object[] chunk = buffer.resetAndStart();
-        final JsonDeserializer<String> deser = _elementDeserializer;
+        int ix;
+        Object[] chunk;
+
+        if (old == null) {
+            ix = 0;
+            chunk = buffer.resetAndStart();
+        } else {
+            ix = old.length;
+            chunk = buffer.resetAndStart(old, ix);
+        }
         
-        int ix = 0;
+        final JsonDeserializer<String> deser = _elementDeserializer;
 
         try {
             while (true) {
@@ -145,7 +205,14 @@
                         break;
                     }
                     // Ok: no need to convert Strings, but must recognize nulls
-                    value = (t == JsonToken.VALUE_NULL) ? deser.getNullValue(ctxt) : deser.deserialize(p, ctxt);
+                    if (t == JsonToken.VALUE_NULL) {
+                        if (_skipNullValues) {
+                            continue;
+                        }
+                        value = (String) _nullProvider.getNullValue(ctxt);
+                    } else {
+                        value = deser.deserialize(p, ctxt);
+                    }
                 } else {
                     value = deser.deserialize(p, ctxt);
                 }
@@ -163,12 +230,68 @@
         ctxt.returnObjectBuffer(buffer);
         return result;
     }
-    
+
     @Override
     public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException {
         return typeDeserializer.deserializeTypedFromArray(p, ctxt);
     }
 
+    @Override
+    public String[] deserialize(JsonParser p, DeserializationContext ctxt,
+            String[] intoValue) throws IOException
+    {
+        // Ok: must point to START_ARRAY (or equivalent)
+        if (!p.isExpectedStartArrayToken()) {
+            String[] arr = handleNonArray(p, ctxt);
+            if (arr == null) {
+                return intoValue;
+            }
+            final int offset = intoValue.length;
+            String[] result = new String[offset + arr.length];
+            System.arraycopy(intoValue, 0, result, 0, offset);
+            System.arraycopy(arr, 0, result, offset, arr.length);
+            return result;
+        }
+
+        if (_elementDeserializer != null) {
+            return _deserializeCustom(p, ctxt, intoValue);
+        }
+        final ObjectBuffer buffer = ctxt.leaseObjectBuffer();
+        int ix = intoValue.length;
+        Object[] chunk = buffer.resetAndStart(intoValue, ix);
+
+        try {
+            while (true) {
+                String value = p.nextTextValue();
+                if (value == null) {
+                    JsonToken t = p.getCurrentToken();
+                    if (t == JsonToken.END_ARRAY) {
+                        break;
+                    }
+                    if (t == JsonToken.VALUE_NULL) {
+                        // 03-Feb-2017, tatu: Should we skip null here or not?
+                        if (_skipNullValues) {
+                            return NO_STRINGS;
+                        }
+                        value = (String) _nullProvider.getNullValue(ctxt);
+                    } else {
+                        value = _parseString(p, ctxt);
+                    }
+                }
+                if (ix >= chunk.length) {
+                    chunk = buffer.appendCompletedChunk(chunk);
+                    ix = 0;
+                }
+                chunk[ix++] = value;
+            }
+        } catch (Exception e) {
+            throw JsonMappingException.wrapWithPath(e, chunk, buffer.bufferedSize() + ix);
+        }
+        String[] result = buffer.completeAndClearBuffer(chunk, ix, String.class);
+        ctxt.returnObjectBuffer(buffer);
+        return result;
+    }
+
     private final String[] handleNonArray(JsonParser p, DeserializationContext ctxt) throws IOException
     {
         // implicit arrays from single values?
@@ -176,8 +299,12 @@
                 ((_unwrapSingle == null) &&
                         ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY));
         if (canWrap) {
-            return new String[] { p.hasToken(JsonToken.VALUE_NULL) ? null : _parseString(p, ctxt) };
-        } else if (p.hasToken(JsonToken.VALUE_STRING)
+            String value = p.hasToken(JsonToken.VALUE_NULL)
+                    ? (String) _nullProvider.getNullValue(ctxt)
+                    : _parseString(p, ctxt);
+            return new String[] { value };
+        }
+        if (p.hasToken(JsonToken.VALUE_STRING)
                     && ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)) {
             String str = p.getText();
             if (str.length() == 0) {
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer.java
index 0ef9714..321df6f 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer.java
@@ -8,6 +8,7 @@
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
 import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
+import com.fasterxml.jackson.databind.deser.NullValueProvider;
 import com.fasterxml.jackson.databind.deser.ValueInstantiator;
 import com.fasterxml.jackson.databind.introspect.AnnotatedWithParams;
 import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
@@ -26,8 +27,6 @@
 
     // // Configuration
 
-    protected final JavaType _collectionType;
-    
     /**
      * Value deserializer to use, if NOT the standard one
      * (if it is, will be null).
@@ -47,15 +46,6 @@
      */
     protected final JsonDeserializer<Object> _delegateDeserializer;
 
-    /**
-     * Specific override for this instance (from proper, or global per-type overrides)
-     * to indicate whether single value may be taken to mean an unwrapped one-element array
-     * or not. If null, left to global defaults.
-     *
-     * @since 2.7
-     */
-    protected final Boolean _unwrapSingle;
-    
     // NOTE: no PropertyBasedCreator, as JSON Arrays have no properties
 
     /*
@@ -67,36 +57,37 @@
     public StringCollectionDeserializer(JavaType collectionType,
             JsonDeserializer<?> valueDeser, ValueInstantiator valueInstantiator)
     {
-        this(collectionType, valueInstantiator, null, valueDeser, null);
+        this(collectionType, valueInstantiator, null, valueDeser, valueDeser, null);
     }
 
     @SuppressWarnings("unchecked")
     protected StringCollectionDeserializer(JavaType collectionType,
             ValueInstantiator valueInstantiator, JsonDeserializer<?> delegateDeser,
-            JsonDeserializer<?> valueDeser, Boolean unwrapSingle)
+            JsonDeserializer<?> valueDeser,
+            NullValueProvider nuller, Boolean unwrapSingle)
     {
-        super(collectionType);
-        _collectionType = collectionType;
+        super(collectionType, nuller, unwrapSingle);
         _valueDeserializer = (JsonDeserializer<String>) valueDeser;
         _valueInstantiator = valueInstantiator;
         _delegateDeserializer = (JsonDeserializer<Object>) delegateDeser;
-        _unwrapSingle = unwrapSingle;
     }
 
     protected StringCollectionDeserializer withResolved(JsonDeserializer<?> delegateDeser,
-            JsonDeserializer<?> valueDeser, Boolean unwrapSingle)
+            JsonDeserializer<?> valueDeser,
+            NullValueProvider nuller, Boolean unwrapSingle)
     {
-        if ((_unwrapSingle == unwrapSingle)
+        if ((_unwrapSingle == unwrapSingle) && (_nullProvider == nuller)
                 && (_valueDeserializer == valueDeser) && (_delegateDeserializer == delegateDeser)) {
             return this;
         }
-        return new StringCollectionDeserializer(_collectionType,
-                _valueInstantiator, delegateDeser, valueDeser, unwrapSingle);
+        return new StringCollectionDeserializer(_containerType, _valueInstantiator,
+                delegateDeser, valueDeser, nuller, unwrapSingle);
     }
 
     @Override // since 2.5
     public boolean isCachable() {
-        // 26-Mar-2015, tatu: Important: prevent caching if custom deserializers are involved
+        // 26-Mar-2015, tatu: Important: prevent caching if custom deserializers via annotations
+        //    are involved
         return (_valueDeserializer == null) && (_delegateDeserializer == null);
     }
     
@@ -119,7 +110,7 @@
             }
         }
         JsonDeserializer<?> valueDeser = _valueDeserializer;
-        final JavaType valueType = _collectionType.getContentType();
+        final JavaType valueType = _containerType.getContentType();
         if (valueDeser == null) {
             // [databind#125]: May have a content converter
             valueDeser = findConvertingContentDeserializer(ctxt, property, valueDeser);
@@ -134,10 +125,11 @@
         //   comes down to "List vs Collection" I suppose... for now, pass Collection
         Boolean unwrapSingle = findFormatFeature(ctxt, property, Collection.class,
                 JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
+        NullValueProvider nuller = findContentNullProvider(ctxt, property, valueDeser);
         if (isDefaultDeserializer(valueDeser)) {
             valueDeser = null;
         }
-        return withResolved(delegate, valueDeser, unwrapSingle);
+        return withResolved(delegate, valueDeser, nuller, unwrapSingle);
     }
     
     /*
@@ -146,18 +138,18 @@
     /**********************************************************
      */
 
-    @Override
-    public JavaType getContentType() {
-        return _collectionType.getContentType();
-    }
-
     @SuppressWarnings("unchecked")
     @Override
     public JsonDeserializer<Object> getContentDeserializer() {
         JsonDeserializer<?> deser = _valueDeserializer;
         return (JsonDeserializer<Object>) deser;
     }
-    
+
+    @Override
+    public ValueInstantiator getValueInstantiator() {
+        return _valueInstantiator;
+    }
+
     /*
     /**********************************************************
     /* JsonDeserializer API
@@ -202,7 +194,12 @@
                 if (t == JsonToken.END_ARRAY) {
                     break;
                 }
-                if (t != JsonToken.VALUE_NULL) {
+                if (t == JsonToken.VALUE_NULL) {
+                    if (_skipNullValues) {
+                        continue;
+                    }
+                    value = (String) _nullProvider.getNullValue(ctxt);
+                } else {
                     value = _parseString(p, ctxt);
                 }
                 result.add(value);
@@ -229,7 +226,14 @@
                     break;
                 }
                 // Ok: no need to convert Strings, but must recognize nulls
-                value = (t == JsonToken.VALUE_NULL) ? deser.getNullValue(ctxt) : deser.deserialize(p, ctxt);
+                if (t == JsonToken.VALUE_NULL) {
+                    if (_skipNullValues) {
+                        continue;
+                    }
+                    value = (String) _nullProvider.getNullValue(ctxt);
+                } else {
+                    value = deser.deserialize(p, ctxt);
+                }
             } else {
                 value = deser.deserialize(p, ctxt);
             }
@@ -239,7 +243,8 @@
     }
     
     @Override
-    public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException {
+    public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
+            TypeDeserializer typeDeserializer) throws IOException {
         // In future could check current token... for now this should be enough:
         return typeDeserializer.deserializeTypedFromArray(p, ctxt);
     }
@@ -250,14 +255,15 @@
      * array, depending on configuration.
      */
     @SuppressWarnings("unchecked")
-    private final Collection<String> handleNonArray(JsonParser p, DeserializationContext ctxt, Collection<String> result) throws IOException
+    private final Collection<String> handleNonArray(JsonParser p, DeserializationContext ctxt,
+            Collection<String> result) throws IOException
     {
         // implicit arrays from single values?
         boolean canWrap = (_unwrapSingle == Boolean.TRUE) ||
                 ((_unwrapSingle == null) &&
                         ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY));
         if (!canWrap) {
-            return (Collection<String>) ctxt.handleUnexpectedToken(_collectionType.getRawClass(), p);
+            return (Collection<String>) ctxt.handleUnexpectedToken(_containerType.getRawClass(), p);
         }
         // Strings are one of "native" (intrinsic) types, so there's never type deserializer involved
         JsonDeserializer<String> valueDes = _valueDeserializer;
@@ -266,7 +272,11 @@
         String value;
         
         if (t == JsonToken.VALUE_NULL) {
-            value = (valueDes == null) ? null : valueDes.getNullValue(ctxt);
+            // 03-Feb-2017, tatu: Does this work?
+            if (_skipNullValues) {
+                return result;
+            }
+            value = (String) _nullProvider.getNullValue(ctxt);
         } else {
             value = (valueDes == null) ? _parseString(p, ctxt) : valueDes.deserialize(p, ctxt);
         }
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/StringDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StringDeserializer.java
index 5082b74..c563f4b 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/StringDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StringDeserializer.java
@@ -3,32 +3,31 @@
 import java.io.IOException;
 
 import com.fasterxml.jackson.core.*;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
 import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
 
 @JacksonStdImpl
-public final class StringDeserializer extends StdScalarDeserializer<String>
+public class StringDeserializer extends StdScalarDeserializer<String> // non-final since 2.9
 {
     private static final long serialVersionUID = 1L;
 
-    // @since 2.8.8
-    protected final static int FEATURES_ACCEPT_ARRAYS =
-            DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS.getMask() |
-            DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT.getMask();
-    
     /**
      * @since 2.2
      */
     public final static StringDeserializer instance = new StringDeserializer();
-    
+
     public StringDeserializer() { super(String.class); }
 
     // since 2.6, slightly faster lookups for this very common type
     @Override
     public boolean isCachable() { return true; }
 
+    @Override // since 2.9
+    public Object getEmptyValue(DeserializationContext ctxt) throws JsonMappingException {
+        return "";
+    }
+
     @Override
     public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
     {
@@ -53,9 +52,13 @@
             return ob.toString();
         }
         // allow coercions for other scalar types
-        String text = p.getValueAsString();
-        if (text != null) {
-            return text;
+        // 17-Jan-2018, tatu: Related to [databind#1853] avoid FIELD_NAME by ensuring it's
+        //   "real" scalar
+        if (t.isScalarValue()) {
+            String text = p.getValueAsString();
+            if (text != null) {
+                return text;
+            }
         }
         return (String) ctxt.handleUnexpectedToken(_valueClass, p);
     }
@@ -63,31 +66,8 @@
     // Since we can never have type info ("natural type"; String, Boolean, Integer, Double):
     // (is it an error to even call this version?)
     @Override
-    public String deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException {
+    public String deserializeWithType(JsonParser p, DeserializationContext ctxt,
+            TypeDeserializer typeDeserializer) throws IOException {
         return deserialize(p, ctxt);
     }
-
-    // @since 2.8.8
-    protected String _deserializeFromArray(JsonParser p, DeserializationContext ctxt) throws IOException
-    {
-        JsonToken t;
-        if (ctxt.hasSomeOfFeatures(FEATURES_ACCEPT_ARRAYS)) {
-            t = p.nextToken();
-            if (t == JsonToken.END_ARRAY) {
-                if (ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT)) {
-                    return getNullValue(ctxt);
-                }
-            }
-            if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
-                final String parsed = _parseString(p, ctxt);
-                if (p.nextToken() != JsonToken.END_ARRAY) {
-                    handleMissingEndArrayForSingle(p, ctxt);
-                }
-                return parsed;            
-            }
-        } else {
-            t = p.getCurrentToken();
-        }
-        return (String) ctxt.handleUnexpectedToken(_valueClass, t, p, null);
-    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/ThrowableDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/ThrowableDeserializer.java
index 0906ad5..d2e8c67 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/ThrowableDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/ThrowableDeserializer.java
@@ -69,14 +69,14 @@
                     _delegateDeserializer.deserialize(p, ctxt));
         }
         if (_beanType.isAbstract()) { // for good measure, check this too
-            return ctxt.handleMissingInstantiator(handledType(), p,
+            return ctxt.handleMissingInstantiator(handledType(), getValueInstantiator(), p,
                     "abstract type (need to add/enable type information?)");
         }
         boolean hasStringCreator = _valueInstantiator.canCreateFromString();
         boolean hasDefaultCtor = _valueInstantiator.canCreateUsingDefault();
         // and finally, verify we do have single-String arg constructor (if no @JsonCreator)
         if (!hasStringCreator && !hasDefaultCtor) {
-            return ctxt.handleMissingInstantiator(handledType(), p,
+            return ctxt.handleMissingInstantiator(handledType(), getValueInstantiator(), p,
                     "Throwable needs a default contructor, a single-String-arg constructor; or explicit @JsonCreator");
         }
         
@@ -105,9 +105,10 @@
             }
 
             // Maybe it's "message"?
-            if (PROP_NAME_MESSAGE.equals(propName)) {
+            final boolean isMessage = PROP_NAME_MESSAGE.equals(propName);
+            if (isMessage) {
                 if (hasStringCreator) {
-                    throwable = _valueInstantiator.createFromString(ctxt, p.getText());
+                    throwable = _valueInstantiator.createFromString(ctxt, p.getValueAsString());
                     // any pending values?
                     if (pending != null) {
                         for (int i = 0, len = pendingIx; i < len; i += 2) {
@@ -128,6 +129,9 @@
                 _anySetter.deserializeAndSet(p, ctxt, throwable, propName);
                 continue;
             }
+            // 23-Jan-2018, tatu: One concern would be `message`, but without any-setter or single-String-ctor
+            //   (or explicit constructor). We could just ignore it but for now, let it fail
+
             // Unknown: let's call handler method
             handleUnknownProperty(p, ctxt, throwable, propName);
         }
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/UntypedObjectDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/UntypedObjectDeserializer.java
index 7f4e220..67be238 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/UntypedObjectDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/UntypedObjectDeserializer.java
@@ -5,6 +5,7 @@
 
 import com.fasterxml.jackson.core.*;
 import com.fasterxml.jackson.databind.BeanProperty;
+import com.fasterxml.jackson.databind.DeserializationConfig;
 import com.fasterxml.jackson.databind.DeserializationContext;
 import com.fasterxml.jackson.databind.DeserializationFeature;
 import com.fasterxml.jackson.databind.JavaType;
@@ -37,12 +38,6 @@
 
     protected final static Object[] NO_OBJECTS = new Object[0];
 
-    /**
-     * @deprecated Since 2.3, construct a new instance, needs to be resolved
-     */
-    @Deprecated
-    public final static UntypedObjectDeserializer instance = new UntypedObjectDeserializer(null, null);
-
     /*
     /**********************************************************
     /* Possible custom deserializer overrides we need to use
@@ -74,6 +69,11 @@
     protected JavaType _mapType;
 
     /**
+     * @since 2.9
+     */
+    protected final boolean _nonMerging;
+    
+    /**
      * @deprecated Since 2.6 use variant takes type arguments
      */
     @Deprecated
@@ -85,6 +85,7 @@
         super(Object.class);
         _listType = listType;
         _mapType = mapType;
+        _nonMerging = false;
     }
 
     @SuppressWarnings("unchecked")
@@ -99,6 +100,23 @@
         _numberDeserializer = (JsonDeserializer<Object>) numberDeser;
         _listType = base._listType;
         _mapType = base._mapType;
+        _nonMerging = base._nonMerging;
+    }
+
+    /**
+     * @since 2.9
+     */
+    protected UntypedObjectDeserializer(UntypedObjectDeserializer base,
+            boolean nonMerging)
+    {
+        super(Object.class);
+        _mapDeserializer = base._mapDeserializer;
+        _listDeserializer = base._listDeserializer;
+        _stringDeserializer = base._stringDeserializer;
+        _numberDeserializer = base._numberDeserializer;
+        _listType = base._listType;
+        _mapType = base._mapType;
+        _nonMerging = nonMerging;
     }
 
     /*
@@ -109,7 +127,7 @@
 
     /**
      * We need to implement this method to properly find things to delegate
-     * to: it can not be done earlier since delegated deserializers almost
+     * to: it cannot be done earlier since delegated deserializers almost
      * certainly require access to this instance (at least "List" and "Map" ones)
      */
     @SuppressWarnings("unchecked")
@@ -174,23 +192,22 @@
     public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
             BeanProperty property) throws JsonMappingException
     {
+        // 14-Jun-2017, tatu: [databind#1625]: may want to block merging, for root value
+        boolean preventMerge = (property == null)
+                && Boolean.FALSE.equals(ctxt.getConfig().getDefaultMergeable(Object.class));
         // 20-Apr-2014, tatu: If nothing custom, let's use "vanilla" instance,
         //     simpler and can avoid some of delegation
         if ((_stringDeserializer == null) && (_numberDeserializer == null)
                 && (_mapDeserializer == null) && (_listDeserializer == null)
                 &&  getClass() == UntypedObjectDeserializer.class) {
-            return Vanilla.std;
+            return Vanilla.instance(preventMerge);
+        }
+        if (preventMerge != _nonMerging) {
+            return new UntypedObjectDeserializer(this, preventMerge);
         }
         return this;
     }
 
-    protected JsonDeserializer<?> _withResolved(JsonDeserializer<?> mapDeser,
-            JsonDeserializer<?> listDeser,
-            JsonDeserializer<?> stringDeser, JsonDeserializer<?> numberDeser) {
-        return new UntypedObjectDeserializer(this,
-                mapDeser, listDeser, stringDeser, numberDeser);
-    }
-
     /*
     /**********************************************************
     /* Deserializer API
@@ -210,6 +227,12 @@
         return true;
     }
 
+    @Override // since 2.9
+    public Boolean supportsUpdate(DeserializationConfig config) {
+        // 21-Apr-2017, tatu: Bit tricky... some values, yes. So let's say "dunno"
+        return null;
+    }
+
     @Override
     public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
     {
@@ -267,7 +290,7 @@
         case JsonTokenId.ID_FALSE:
             return Boolean.FALSE;
 
-        case JsonTokenId.ID_NULL: // should not get this but...
+        case JsonTokenId.ID_NULL: // 08-Nov-2016, tatu: yes, occurs
             return null;
 
 //        case JsonTokenId.ID_END_ARRAY: // invalid
@@ -277,16 +300,15 @@
     }
 
     @Override
-    public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException
+    public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
+            TypeDeserializer typeDeserializer) throws IOException
     {
         switch (p.getCurrentTokenId()) {
         // First: does it look like we had type id wrapping of some kind?
         case JsonTokenId.ID_START_ARRAY:
         case JsonTokenId.ID_START_OBJECT:
         case JsonTokenId.ID_FIELD_NAME:
-            /* Output can be as JSON Object, Array or scalar: no way to know
-             * a this point:
-             */
+            // Output can be as JSON Object, Array or scalar: no way to know at this point:
             return typeDeserializer.deserializeTypedFromAny(p, ctxt);
 
         case JsonTokenId.ID_EMBEDDED_OBJECT:
@@ -332,6 +354,78 @@
         return ctxt.handleUnexpectedToken(Object.class, p);
     }
 
+    @SuppressWarnings("unchecked")
+    @Override // since 2.9 (to support deep merge)
+    public Object deserialize(JsonParser p, DeserializationContext ctxt, Object intoValue)
+        throws IOException
+    {
+        if (_nonMerging) {
+            return deserialize(p, ctxt);
+        }
+
+        switch (p.getCurrentTokenId()) {
+        case JsonTokenId.ID_START_OBJECT:
+        case JsonTokenId.ID_FIELD_NAME:
+            // 28-Oct-2015, tatu: [databind#989] We may also be given END_OBJECT (similar to FIELD_NAME),
+            //    if caller has advanced to the first token of Object, but for empty Object
+        case JsonTokenId.ID_END_OBJECT:
+            if (_mapDeserializer != null) {
+                return _mapDeserializer.deserialize(p, ctxt, intoValue);
+            }
+            if (intoValue instanceof Map<?,?>) {
+                return mapObject(p, ctxt, (Map<Object,Object>) intoValue);
+            }
+            return mapObject(p, ctxt);
+        case JsonTokenId.ID_START_ARRAY:
+            if (_listDeserializer != null) {
+                return _listDeserializer.deserialize(p, ctxt, intoValue);
+            }
+            if (intoValue instanceof Collection<?>) {
+                return mapArray(p, ctxt, (Collection<Object>) intoValue);
+            }
+            if (ctxt.isEnabled(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY)) {
+                return mapArrayToArray(p, ctxt);
+            }
+            return mapArray(p, ctxt);
+        case JsonTokenId.ID_EMBEDDED_OBJECT:
+            return p.getEmbeddedObject();
+        case JsonTokenId.ID_STRING:
+            if (_stringDeserializer != null) {
+                return _stringDeserializer.deserialize(p, ctxt, intoValue);
+            }
+            return p.getText();
+
+        case JsonTokenId.ID_NUMBER_INT:
+            if (_numberDeserializer != null) {
+                return _numberDeserializer.deserialize(p, ctxt, intoValue);
+            }
+            if (ctxt.hasSomeOfFeatures(F_MASK_INT_COERCIONS)) {
+                return _coerceIntegral(p, ctxt);
+            }
+            return p.getNumberValue();
+
+        case JsonTokenId.ID_NUMBER_FLOAT:
+            if (_numberDeserializer != null) {
+                return _numberDeserializer.deserialize(p, ctxt, intoValue);
+            }
+            if (ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) {
+                return p.getDecimalValue();
+            }
+            return p.getNumberValue();
+        case JsonTokenId.ID_TRUE:
+            return Boolean.TRUE;
+        case JsonTokenId.ID_FALSE:
+            return Boolean.FALSE;
+
+        case JsonTokenId.ID_NULL:
+            // 21-Apr-2017, tatu: May need to consider "skip nulls" at some point but...
+            return null;
+        default:
+        }
+        // easiest to just delegate to "dumb" version for the rest?
+        return deserialize(p, ctxt);
+    }
+
     /*
     /**********************************************************
     /* Internal methods
@@ -381,6 +475,17 @@
         return result;
     }
 
+    protected Object mapArray(JsonParser p, DeserializationContext ctxt,
+            Collection<Object> result) throws IOException
+    {
+        // we start by pointing to START_ARRAY. Also, no real merging; array/Collection
+        // just appends always
+        while (p.nextToken() != JsonToken.END_ARRAY) {
+            result.add(deserialize(p, ctxt));
+        }
+        return result;
+    }
+
     /**
      * Method called to map a JSON Object into a Java value.
      */
@@ -463,6 +568,36 @@
         return buffer.completeAndClearBuffer(values, ptr);
     }
 
+    protected Object mapObject(JsonParser p, DeserializationContext ctxt,
+            Map<Object,Object> m) throws IOException
+    {
+        JsonToken t = p.getCurrentToken();
+        if (t == JsonToken.START_OBJECT) {
+            t = p.nextToken();
+        }
+        if (t == JsonToken.END_OBJECT) {
+            return m;
+        }
+        // NOTE: we are guaranteed to point to FIELD_NAME
+        String key = p.getCurrentName();
+        do {
+            p.nextToken();
+            // and possibly recursive merge here
+            Object old = m.get(key);
+            Object newV;
+
+            if (old != null) {
+                newV = deserialize(p, ctxt, old);
+            } else {
+                newV = deserialize(p, ctxt);
+            }
+            if (newV != old) {
+                m.put(key, newV);
+            }
+        } while ((key = p.nextFieldName()) != null);
+        return m;
+    }
+
     /*
     /**********************************************************
     /* Separate "vanilla" implementation for common case of
@@ -478,7 +613,31 @@
 
         public final static Vanilla std = new Vanilla();
 
-        public Vanilla() { super(Object.class); }
+        /**
+         * @since 2.9
+         */
+        protected final boolean _nonMerging;
+        
+        public Vanilla() { this(false); }
+
+        protected Vanilla(boolean nonMerging) {
+            super(Object.class);
+            _nonMerging = nonMerging;
+        }
+
+        public static Vanilla instance(boolean nonMerging) {
+            if (nonMerging) {
+                return new Vanilla(true);
+            }
+            return std;
+        }
+        
+        @Override // since 2.9
+        public Boolean supportsUpdate(DeserializationConfig config) {
+            // 21-Apr-2017, tatu: Bit tricky... some values, yes. So let's say "dunno"
+            // 14-Jun-2017, tatu: Well, if merging blocked, can say no, as well.
+            return _nonMerging ? Boolean.FALSE : null;
+        }
 
         @Override
         public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
@@ -529,14 +688,14 @@
             case JsonTokenId.ID_FALSE:
                 return Boolean.FALSE;
 
-            case JsonTokenId.ID_NULL: // should not get this but...
-                return null;
-
             case JsonTokenId.ID_END_OBJECT:
                 // 28-Oct-2015, tatu: [databind#989] We may also be given END_OBJECT (similar to FIELD_NAME),
                 //    if caller has advanced to the first token of Object, but for empty Object
                 return new LinkedHashMap<String,Object>(2);
 
+            case JsonTokenId.ID_NULL: // 08-Nov-2016, tatu: yes, occurs
+                return null;
+
             //case JsonTokenId.ID_END_ARRAY: // invalid
             default:
             }
@@ -581,6 +740,72 @@
             return ctxt.handleUnexpectedToken(Object.class, p);
         }
 
+        @SuppressWarnings("unchecked")
+        @Override // since 2.9 (to support deep merge)
+        public Object deserialize(JsonParser p, DeserializationContext ctxt, Object intoValue)
+            throws IOException
+        {
+            if (_nonMerging) {
+                return deserialize(p, ctxt);
+            }
+
+            switch (p.getCurrentTokenId()) {
+            case JsonTokenId.ID_END_OBJECT:
+            case JsonTokenId.ID_END_ARRAY:
+                return intoValue;
+            case JsonTokenId.ID_START_OBJECT:
+                {
+                    JsonToken t = p.nextToken(); // to get to FIELD_NAME or END_OBJECT
+                    if (t == JsonToken.END_OBJECT) {
+                        return intoValue;
+                    }
+                }
+            case JsonTokenId.ID_FIELD_NAME:
+                if (intoValue instanceof Map<?,?>) {
+                    Map<Object,Object> m = (Map<Object,Object>) intoValue;
+                    // NOTE: we are guaranteed to point to FIELD_NAME
+                    String key = p.getCurrentName();
+                    do {
+                        p.nextToken();
+                        // and possibly recursive merge here
+                        Object old = m.get(key);
+                        Object newV;
+                        if (old != null) {
+                            newV = deserialize(p, ctxt, old);
+                        } else {
+                            newV = deserialize(p, ctxt);
+                        }
+                        if (newV != old) {
+                            m.put(key, newV);
+                        }
+                    } while ((key = p.nextFieldName()) != null);
+                    return intoValue;
+                }
+                break;
+            case JsonTokenId.ID_START_ARRAY:
+                {
+                    JsonToken t = p.nextToken(); // to get to FIELD_NAME or END_OBJECT
+                    if (t == JsonToken.END_ARRAY) {
+                        return intoValue;
+                    }
+                }
+
+                if (intoValue instanceof Collection<?>) {
+                    Collection<Object> c = (Collection<Object>) intoValue;
+                    // NOTE: merge for arrays/Collections means append, can't merge contents
+                    do {
+                        c.add(deserialize(p, ctxt));
+                    } while (p.nextToken() != JsonToken.END_ARRAY);
+                    return intoValue;
+                }
+                // 21-Apr-2017, tatu: Should we try to support merging of Object[] values too?
+                //    ... maybe future improvement
+                break;
+            }
+            // Easiest handling for the rest, delegate. Only (?) question: how about nulls?
+            return deserialize(p, ctxt);
+        }
+
         protected Object mapArray(JsonParser p, DeserializationContext ctxt) throws IOException
         {
             Object value = deserialize(p, ctxt);
@@ -618,6 +843,24 @@
         }
 
         /**
+         * Method called to map a JSON Array into a Java Object array (Object[]).
+         */
+        protected Object[] mapArrayToArray(JsonParser p, DeserializationContext ctxt) throws IOException {
+            ObjectBuffer buffer = ctxt.leaseObjectBuffer();
+            Object[] values = buffer.resetAndStart();
+            int ptr = 0;
+            do {
+                Object value = deserialize(p, ctxt);
+                if (ptr >= values.length) {
+                    values = buffer.appendCompletedChunk(values);
+                    ptr = 0;
+                }
+                values[ptr++] = value;
+            } while (p.nextToken() != JsonToken.END_ARRAY);
+            return buffer.completeAndClearBuffer(values, ptr);
+        }
+
+        /**
          * Method called to map a JSON Object into a Java value.
          */
         protected Object mapObject(JsonParser p, DeserializationContext ctxt) throws IOException
@@ -653,23 +896,5 @@
             } while ((key = p.nextFieldName()) != null);
             return result;
         }
-
-        /**
-         * Method called to map a JSON Array into a Java Object array (Object[]).
-         */
-        protected Object[] mapArrayToArray(JsonParser p, DeserializationContext ctxt) throws IOException {
-            ObjectBuffer buffer = ctxt.leaseObjectBuffer();
-            Object[] values = buffer.resetAndStart();
-            int ptr = 0;
-            do {
-                Object value = deserialize(p, ctxt);
-                if (ptr >= values.length) {
-                    values = buffer.appendCompletedChunk(values);
-                    ptr = 0;
-                }
-                values[ptr++] = value;
-            } while (p.nextToken() != JsonToken.END_ARRAY);
-            return buffer.completeAndClearBuffer(values, ptr);
-        }
     }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/exc/IgnoredPropertyException.java b/src/main/java/com/fasterxml/jackson/databind/exc/IgnoredPropertyException.java
index c26d951..e78ad52 100644
--- a/src/main/java/com/fasterxml/jackson/databind/exc/IgnoredPropertyException.java
+++ b/src/main/java/com/fasterxml/jackson/databind/exc/IgnoredPropertyException.java
@@ -39,7 +39,6 @@
         super(msg, loc, referringClass, propName, propertyIds);
     }
 
-    
     /**
      * Factory method used for constructing instances of this exception type.
      * 
@@ -48,23 +47,20 @@
      *    if available), or if not, type itself
      * @param propertyName Name of unrecognized property
      * @param propertyIds (optional, null if not available) Set of properties that
-     *    type would recognize, if completely known: null if set can not be determined.
+     *    type would recognize, if completely known: null if set cannot be determined.
      */
     public static IgnoredPropertyException from(JsonParser p,
             Object fromObjectOrClass, String propertyName,
             Collection<Object> propertyIds)
     {
-        if (fromObjectOrClass == null) {
-            throw new IllegalArgumentException();
-        }
         Class<?> ref;
         if (fromObjectOrClass instanceof Class<?>) {
             ref = (Class<?>) fromObjectOrClass;
-        } else {
+        } else { // also acts as null check:
             ref = fromObjectOrClass.getClass();
         }
-        String msg = "Ignored field \""+propertyName+"\" (class "+ref.getName()
-                +") encountered; mapper configured not to allow this";
+        String msg = String.format("Ignored field \"%s\" (class %s) encountered; mapper configured not to allow this",
+                propertyName, ref.getName());
         IgnoredPropertyException e = new IgnoredPropertyException(p, msg,
                 p.getCurrentLocation(), ref, propertyName, propertyIds);
         // but let's also ensure path includes this last (missing) segment
diff --git a/src/main/java/com/fasterxml/jackson/databind/exc/InvalidDefinitionException.java b/src/main/java/com/fasterxml/jackson/databind/exc/InvalidDefinitionException.java
new file mode 100644
index 0000000..444e6d9
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/exc/InvalidDefinitionException.java
@@ -0,0 +1,104 @@
+package com.fasterxml.jackson.databind.exc;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.BeanDescription;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
+
+/**
+ * Intermediate exception type used as the base class for all {@link JsonMappingException}s
+ * that are due to problems with target type definition; usually a problem with
+ * annotations used on a class or its properties.
+ * This is in contrast to {@link MismatchedInputException} which
+ * signals a problem with input to map.
+ *
+ * @since 2.9
+ */
+@SuppressWarnings("serial")
+public class InvalidDefinitionException
+    extends JsonMappingException
+{
+    protected final JavaType _type;
+
+    protected transient BeanDescription _beanDesc;
+    protected transient BeanPropertyDefinition _property;
+
+    protected InvalidDefinitionException(JsonParser p, String msg,
+            JavaType type) {
+        super(p, msg);
+        _type = type;
+        _beanDesc = null;
+        _property = null;
+    }
+
+    protected InvalidDefinitionException(JsonGenerator g, String msg,
+            JavaType type) {
+        super(g, msg);
+        _type = type;
+        _beanDesc = null;
+        _property = null;
+    }
+
+    protected InvalidDefinitionException(JsonParser p, String msg,
+            BeanDescription bean, BeanPropertyDefinition prop) {
+        super(p, msg);
+        _type = (bean == null) ? null : bean.getType();
+        _beanDesc = bean;
+        _property = prop;
+    }
+
+    protected InvalidDefinitionException(JsonGenerator g, String msg,
+            BeanDescription bean, BeanPropertyDefinition prop) {
+        super(g, msg);
+        _type = (bean == null) ? null : bean.getType();
+        _beanDesc = bean;
+        _property = prop;
+    }
+
+    public static InvalidDefinitionException from(JsonParser p, String msg,
+            BeanDescription bean, BeanPropertyDefinition prop) {
+        return new InvalidDefinitionException(p, msg, bean, prop);
+    }
+
+    public static InvalidDefinitionException from(JsonParser p, String msg,
+            JavaType type) {
+        return new InvalidDefinitionException(p, msg, type);
+    }
+
+    public static InvalidDefinitionException from(JsonGenerator g, String msg,
+            BeanDescription bean, BeanPropertyDefinition prop) {
+        return new InvalidDefinitionException(g, msg, bean, prop);
+    }
+
+    public static InvalidDefinitionException from(JsonGenerator g, String msg,
+            JavaType type) {
+        return new InvalidDefinitionException(g, msg, type);
+    }
+
+    /**
+     * Accessor for type fully resolved type that had the problem; this should always
+     * known and available, never <code>null</code>
+     */
+    public JavaType getType() {
+        return _type;
+    }
+
+    /**
+     * Accessor for type definition (class) that had the definition problem, if any; may sometimes
+     * be undefined or unknown; if so, returns <code>null</code>.
+     */
+    public BeanDescription getBeanDescription() {
+        return _beanDesc;
+    }
+
+    /**
+     * Accessor for property that had the definition problem if any
+     * (none, for example if the problem relates to type in general),
+     * if known. If not known (or relevant), returns <code>null</code>.
+     */
+    public BeanPropertyDefinition getProperty() {
+        return _property;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/exc/InvalidFormatException.java b/src/main/java/com/fasterxml/jackson/databind/exc/InvalidFormatException.java
index e7593d1..b1b8b10 100644
--- a/src/main/java/com/fasterxml/jackson/databind/exc/InvalidFormatException.java
+++ b/src/main/java/com/fasterxml/jackson/databind/exc/InvalidFormatException.java
@@ -2,16 +2,16 @@
 
 import com.fasterxml.jackson.core.JsonLocation;
 import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.databind.JsonMappingException;
 
 /**
- * Specialized sub-class of {@link JsonMappingException}
+ * Specialized sub-class of {@link MismatchedInputException}
  * that is used when the underlying problem appears to be that
  * of bad formatting of a value to deserialize.
  * 
  * @since 2.1
  */
-public class InvalidFormatException extends JsonMappingException
+public class InvalidFormatException
+    extends MismatchedInputException // since 2.9
 {
     private static final long serialVersionUID = 1L; // silly Eclipse, warnings
 
@@ -21,12 +21,6 @@
      */
     protected final Object _value;
 
-    /**
-     * Intended target type (type-erased class) that value could not
-     * be deserialized into, if known.
-     */
-    protected final Class<?> _targetType;
-    
     /*
     /**********************************************************
     /* Life-cycle
@@ -63,9 +57,8 @@
     public InvalidFormatException(JsonParser p,
             String msg, Object value, Class<?> targetType)
     {
-        super(p, msg);
+        super(p, msg, targetType);
         _value = value;
-        _targetType = targetType;
     }
 
     public static InvalidFormatException from(JsonParser p, String msg,
@@ -89,14 +82,4 @@
     public Object getValue() {
         return _value;
     }
-
-    /**
-     * Accessor for checking target type of value ({@link #getValue} that failed
-     * to deserialize.
-     * Note that type may not be available, depending on who throws the exception
-     * and when.
-     */
-    public Class<?> getTargetType() {
-        return _targetType;
-    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/exc/InvalidNullException.java b/src/main/java/com/fasterxml/jackson/databind/exc/InvalidNullException.java
new file mode 100644
index 0000000..daa0eeb
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/exc/InvalidNullException.java
@@ -0,0 +1,52 @@
+package com.fasterxml.jackson.databind.exc;
+
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.PropertyName;
+import com.fasterxml.jackson.databind.util.ClassUtil;
+
+/**
+ * Exception thrown if a `null` value is being encountered for a property
+ * designed as "fail on null" property (see {@link com.fasterxml.jackson.annotation.JsonSetter}).
+ *
+ * @since 2.9
+ */
+public class InvalidNullException
+    extends MismatchedInputException // since 2.9
+{
+    private static final long serialVersionUID = 1L; // silly Eclipse, warnings
+
+    /**
+     * Name of property, if known, for which null was encountered.
+     */
+    protected final PropertyName _propertyName;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    protected InvalidNullException(DeserializationContext ctxt, String msg,
+            PropertyName pname)
+    {
+        super(ctxt.getParser(), msg);
+        _propertyName = pname;
+    }
+
+    public static InvalidNullException from(DeserializationContext ctxt,
+            PropertyName name, JavaType type)
+    {
+        String msg = String.format("Invalid `null` value encountered for property %s",
+                ClassUtil.quotedOr(name, "<UNKNOWN>"));
+        InvalidNullException exc = new InvalidNullException(ctxt, msg, name);
+        if (type != null) {
+            exc.setTargetType(type);
+        }
+        return exc;
+    }
+
+    public PropertyName getPropertyName() {
+        return _propertyName;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/exc/InvalidTypeIdException.java b/src/main/java/com/fasterxml/jackson/databind/exc/InvalidTypeIdException.java
index 8b05215..d405a43 100644
--- a/src/main/java/com/fasterxml/jackson/databind/exc/InvalidTypeIdException.java
+++ b/src/main/java/com/fasterxml/jackson/databind/exc/InvalidTypeIdException.java
@@ -2,14 +2,14 @@
 
 import com.fasterxml.jackson.core.JsonParser;
 import com.fasterxml.jackson.databind.JavaType;
-import com.fasterxml.jackson.databind.JsonMappingException;
 
 /**
  * Exception thrown when resolution of a type id fails.
  *
  * @since 2.8
  */
-public class InvalidTypeIdException extends JsonMappingException
+public class InvalidTypeIdException
+    extends MismatchedInputException // since 2.9
 {
     private static final long serialVersionUID = 1L; // silly Eclipse, warnings
 
@@ -19,7 +19,8 @@
     protected final JavaType _baseType;
 
     /**
-     * Type id that failed to be resolved to a subtype
+     * Type id that failed to be resolved to a subtype; `null` in cases
+     * where no type id was located (since 2.9).
      */
     protected final String _typeId;
 
diff --git a/src/main/java/com/fasterxml/jackson/databind/exc/MismatchedInputException.java b/src/main/java/com/fasterxml/jackson/databind/exc/MismatchedInputException.java
new file mode 100644
index 0000000..183831a
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/exc/MismatchedInputException.java
@@ -0,0 +1,78 @@
+package com.fasterxml.jackson.databind.exc;
+
+import com.fasterxml.jackson.core.JsonLocation;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.util.ClassUtil;
+
+/**
+ * General exception type used as the base class for all {@link JsonMappingException}s
+ * that are due to input not mapping to target definition; these are typically
+ * considered "client errors" since target type definition itself is not the root cause
+ * but mismatching input. This is in contrast to {@link InvalidDefinitionException} which
+ * signals a problem with target type definition and not input.
+ *<p>
+ * This type is used as-is for some input problems, but in most cases there should be
+ * more explicit subtypes to use.
+ *<p>
+ * NOTE: name chosen to differ from `java.util.InputMismatchException` since while that
+ * would have been better name, use of same overlapping name causes nasty issues
+ * with IDE auto-completion, so slightly less optimal chosen.
+ *
+ * @since 2.9
+ */
+@SuppressWarnings("serial")
+public class MismatchedInputException
+    extends JsonMappingException
+{
+    /**
+     * Type of value that was to be deserialized
+     */
+    protected Class<?> _targetType;
+
+    protected MismatchedInputException(JsonParser p, String msg) {
+        this(p, msg, (JavaType) null);
+    }
+
+    protected MismatchedInputException(JsonParser p, String msg, JsonLocation loc) {
+        super(p, msg, loc);
+    }
+
+    protected MismatchedInputException(JsonParser p, String msg, Class<?> targetType) {
+        super(p, msg);
+        _targetType = targetType;
+    }
+
+    protected MismatchedInputException(JsonParser p, String msg, JavaType targetType) {
+        super(p, msg);
+        _targetType = ClassUtil.rawClass(targetType);
+    }
+
+    // Only to prevent super-class static method from getting called
+    @Deprecated // as of 2.9
+    public static MismatchedInputException from(JsonParser p, String msg) {
+        return from(p, (Class<?>) null, msg);
+    }
+
+    public static MismatchedInputException from(JsonParser p, JavaType targetType, String msg) {
+        return new MismatchedInputException(p, msg, targetType);
+    }
+
+    public static MismatchedInputException from(JsonParser p, Class<?> targetType, String msg) {
+        return new MismatchedInputException(p, msg, targetType);
+    }
+
+    public MismatchedInputException setTargetType(JavaType t) {
+        _targetType = t.getRawClass();
+        return this;
+    }
+
+    /**
+     * Accessor for getting intended target type, with which input did not match,
+     * if known; `null` if not known for some reason.
+     */
+    public Class<?> getTargetType() {
+        return _targetType;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/exc/PropertyBindingException.java b/src/main/java/com/fasterxml/jackson/databind/exc/PropertyBindingException.java
index 2dcfe8a..7e5c630 100644
--- a/src/main/java/com/fasterxml/jackson/databind/exc/PropertyBindingException.java
+++ b/src/main/java/com/fasterxml/jackson/databind/exc/PropertyBindingException.java
@@ -16,7 +16,7 @@
  */
 @SuppressWarnings("serial")
 public abstract class PropertyBindingException
-    extends JsonMappingException
+    extends MismatchedInputException // since 2.9
 {
     /**
      * Class that does not contain mapping for the unrecognized property.
diff --git a/src/main/java/com/fasterxml/jackson/databind/exc/UnrecognizedPropertyException.java b/src/main/java/com/fasterxml/jackson/databind/exc/UnrecognizedPropertyException.java
index d913079..fda5674 100644
--- a/src/main/java/com/fasterxml/jackson/databind/exc/UnrecognizedPropertyException.java
+++ b/src/main/java/com/fasterxml/jackson/databind/exc/UnrecognizedPropertyException.java
@@ -43,22 +43,20 @@
      *    if available), or if not, type itself
      * @param propertyName Name of unrecognized property
      * @param propertyIds (optional, null if not available) Set of properties that
-     *    type would recognize, if completely known: null if set can not be determined.
+     *    type would recognize, if completely known: null if set cannot be determined.
      */
     public static UnrecognizedPropertyException from(JsonParser p,
             Object fromObjectOrClass, String propertyName,
             Collection<Object> propertyIds)
     {
-        if (fromObjectOrClass == null) {
-            throw new IllegalArgumentException();
-        }
         Class<?> ref;
         if (fromObjectOrClass instanceof Class<?>) {
             ref = (Class<?>) fromObjectOrClass;
         } else {
             ref = fromObjectOrClass.getClass();
         }
-        String msg = "Unrecognized field \""+propertyName+"\" (class "+ref.getName()+"), not marked as ignorable";
+        String msg = String.format("Unrecognized field \"%s\" (class %s), not marked as ignorable",
+                propertyName, ref.getName());
         UnrecognizedPropertyException e = new UnrecognizedPropertyException(p, msg,
                 p.getCurrentLocation(), ref, propertyName, propertyIds);
         // but let's also ensure path includes this last (missing) segment
diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/Java7Support.java b/src/main/java/com/fasterxml/jackson/databind/ext/Java7Support.java
index 5edf8a0..051f570 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ext/Java7Support.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ext/Java7Support.java
@@ -5,6 +5,7 @@
 import com.fasterxml.jackson.databind.PropertyName;
 import com.fasterxml.jackson.databind.introspect.Annotated;
 import com.fasterxml.jackson.databind.introspect.AnnotatedParameter;
+import com.fasterxml.jackson.databind.util.ClassUtil;
 
 /**
  * To support Java7-incomplete platforms, we will offer support for JDK 7
@@ -21,7 +22,7 @@
         Java7Support impl = null;
         try {
             Class<?> cls = Class.forName("com.fasterxml.jackson.databind.ext.Java7SupportImpl");
-            impl = (Java7Support) cls.newInstance();
+            impl = (Java7Support) ClassUtil.createInstance(cls, false);
         } catch (Throwable t) {
             // 24-Nov-2015, tatu: Should we log or not?
             java.util.logging.Logger.getLogger(Java7Support.class.getName())
diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/NioPathSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ext/NioPathSerializer.java
index 1abfab5..b8c23a5 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ext/NioPathSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ext/NioPathSerializer.java
@@ -5,7 +5,11 @@
 import java.nio.file.Path;
 
 import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.core.type.WritableTypeId;
+
 import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
 import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer;
 
 /**
@@ -22,4 +26,17 @@
         // write the Path as a URI, always.
         gen.writeString(value.toUri().toString());
     }
+
+    // [databind#1688]: Not sure this is 100% ok, considering there are legitimately different
+    //  impls... but has to do
+    @Override
+    public void serializeWithType(Path value, JsonGenerator g,
+            SerializerProvider provider, TypeSerializer typeSer) throws IOException
+    {
+        // Better ensure we don't use specific sub-classes:
+        WritableTypeId typeIdDef = typeSer.writeTypePrefix(g,
+                typeSer.typeId(value, Path.class, JsonToken.VALUE_STRING));
+        serialize(value, g, provider);
+        typeSer.writeTypeSuffix(g, typeIdDef);
+    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/OptionalHandlerFactory.java b/src/main/java/com/fasterxml/jackson/databind/ext/OptionalHandlerFactory.java
index 0f49a22..011c31f 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ext/OptionalHandlerFactory.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ext/OptionalHandlerFactory.java
@@ -6,6 +6,7 @@
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.deser.Deserializers;
 import com.fasterxml.jackson.databind.ser.Serializers;
+import com.fasterxml.jackson.databind.util.ClassUtil;
 
 /**
  * Helper class used for isolating details of handling optional+external types
@@ -153,7 +154,7 @@
     private Object instantiate(String className)
     {
         try {
-            return Class.forName(className).newInstance();
+            return ClassUtil.createInstance(Class.forName(className), false);
         } catch (LinkageError e) { }
         // too many different kinds to enumerate here:
         catch (Exception e) { }
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/Annotated.java b/src/main/java/com/fasterxml/jackson/databind/introspect/Annotated.java
index 2d7c064..fce7b9e 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/Annotated.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/Annotated.java
@@ -24,20 +24,6 @@
      * @since 2.7
      */
     public abstract boolean hasOneOf(Class<? extends Annotation>[] annoClasses);
-    
-    /**
-     * Fluent factory method that will construct a new instance that uses specified
-     * instance annotations instead of currently configured ones.
-     */
-    public abstract Annotated withAnnotations(AnnotationMap fallback);
-
-    /**
-     * Fluent factory method that will construct a new instance that uses
-     * annotations from specified {@link Annotated} as fallback annotations
-     */
-    public final Annotated withFallBackAnnotationsFrom(Annotated annotated) {
-        return withAnnotations(AnnotationMap.merge(getAllAnnotations(), annotated.getAllAnnotations()));
-    }
 
     /**
      * Method that can be used to find actual JDK element that this instance
@@ -48,7 +34,7 @@
 
     protected abstract int getModifiers();
 
-    public final boolean isPublic() {
+    public boolean isPublic() {
         return Modifier.isPublic(getModifiers());
     }
 
@@ -73,7 +59,7 @@
     /**
      * JDK declared generic type of the annotated element; definition
      * of what exactly this means depends on sub-class. Note that such type
-     * can not be reliably resolved without {@link TypeResolutionContext}, and
+     * cannot be reliably resolved without {@link TypeResolutionContext}, and
      * as a result use of this method was deprecated in Jackson 2.7: see
      * {@link #getType} for replacement.
      *
@@ -93,17 +79,13 @@
     /**
      * Accessor that can be used to iterate over all the annotations
      * associated with annotated component.
-     * 
+     *
      * @since 2.3
+     * @deprecated Since 2.9 should instead use {@link #getAnnotated()}
      */
+    @Deprecated
     public abstract Iterable<Annotation> annotations();
 
-    /**
-     * Internal helper method used to access annotation information;
-     * not exposed to developers since instances are mutable.
-     */
-    protected abstract AnnotationMap getAllAnnotations();
-
     // Also: ensure we can use #equals, #hashCode
     
     @Override
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java
index 109c4c7..2c8b733 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java
@@ -1,8 +1,6 @@
 package com.fasterxml.jackson.databind.introspect;
 
 import java.lang.annotation.Annotation;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
 import java.lang.reflect.*;
 import java.util.*;
 
@@ -19,7 +17,9 @@
     extends Annotated
     implements TypeResolutionContext
 {
-    private final static AnnotationMap[] NO_ANNOTATION_MAPS = new AnnotationMap[0];
+    private final static Creators NO_CREATORS = new Creators(null,
+            Collections.<AnnotatedConstructor>emptyList(),
+            Collections.<AnnotatedMethod>emptyList());
 
     /*
     /**********************************************************
@@ -48,8 +48,6 @@
     /**
      * Ordered set of super classes and interfaces of the
      * class itself: included in order of precedence
-     *<p>
-     * NOTE: changed in 2.7 from List of <code>Class</code>es to List of {@link JavaType}s.
      */
     final protected List<JavaType> _superTypes;
 
@@ -88,29 +86,12 @@
      * Combined list of Jackson annotations that the class has,
      * including inheritable ones from super classes and interfaces
      */
-    final protected AnnotationMap _classAnnotations;
+    final protected Annotations _classAnnotations;
 
     /**
-     * Flag to indicate whether creator information has been resolved
-     * or not.
+     * @since 2.9
      */
-    protected boolean _creatorsResolved = false;
-    
-    /**
-     * Default constructor of the annotated class, if it has one.
-     */
-    protected AnnotatedConstructor _defaultConstructor;
-
-    /**
-     * Single argument constructors the class has, if any.
-     */
-    protected List<AnnotatedConstructor> _constructors;
-
-    /**
-     * Single argument static methods that might be usable
-     * as factory methods
-     */
-    protected List<AnnotatedMethod> _creatorMethods;
+    protected Creators _creators;
 
     /**
      * Member methods of interest; for now ones with 0 or 1 arguments
@@ -141,98 +122,83 @@
     /**
      * Constructor will not do any initializations, to allow for
      * configuring instances differently depending on use cases
+     *
+     * @param type Fully resolved type; may be `null`, but ONLY if no member fields or
+     *    methods are to be accessed
+     * @param rawType Type-erased class; pass if no `type` needed or available
      */
-    private AnnotatedClass(JavaType type, Class<?> rawType, TypeBindings bindings,
-            List<JavaType> superTypes,
+    AnnotatedClass(JavaType type, Class<?> rawType, List<JavaType> superTypes,
+            Class<?> primaryMixIn, Annotations classAnnotations, TypeBindings bindings, 
             AnnotationIntrospector aintr, MixInResolver mir, TypeFactory tf)
     {
         _type = type;
         _class = rawType;
-        _bindings = bindings;
         _superTypes = superTypes;
+        _primaryMixIn = primaryMixIn;
+        _classAnnotations = classAnnotations;
+        _bindings = bindings;
         _annotationIntrospector = aintr;
-        _typeFactory = tf;
         _mixInResolver = mir;
-        _primaryMixIn = (_mixInResolver == null) ? null
-            : _mixInResolver.findMixInClassFor(_class);
-        _classAnnotations = _resolveClassAnnotations();
-    }
-
-    private AnnotatedClass(AnnotatedClass base, AnnotationMap clsAnn) {
-        _type = base._type;
-        _class = base._class;
-        _bindings = base._bindings;
-        _superTypes = base._superTypes;
-        _annotationIntrospector = base._annotationIntrospector;
-        _typeFactory = base._typeFactory;
-        _mixInResolver = base._mixInResolver;
-        _primaryMixIn = base._primaryMixIn;
-        _classAnnotations = clsAnn;
-    }
-
-    @Override
-    public AnnotatedClass withAnnotations(AnnotationMap ann) {
-        return new AnnotatedClass(this, ann);
+        _typeFactory = tf;
     }
 
     /**
-     * Factory method that instantiates an instance. Returned instance
-     * will only be initialized with class annotations, but not with
-     * any method information.
-     * 
-     * @since 2.7
+     * Constructor (only) used for creating primordial simple types (during bootstrapping)
+     * and array type placeholders where no fields or methods are needed.
+     *
+     * @since 2.9
      */
+    AnnotatedClass(Class<?> rawType) {
+        _type = null;
+        _class = rawType;
+        _superTypes = Collections.emptyList();
+        _primaryMixIn = null;
+        _classAnnotations = AnnotationCollector.emptyAnnotations();
+        _bindings = TypeBindings.emptyBindings();
+        _annotationIntrospector = null;
+        _mixInResolver = null;
+        _typeFactory = null;
+    }
+
+    /**
+     * @deprecated Since 2.9, use methods in {@link AnnotatedClassResolver} instead.
+     */
+    @Deprecated
     public static AnnotatedClass construct(JavaType type, MapperConfig<?> config) {
-        AnnotationIntrospector intr = config.isAnnotationProcessingEnabled()
-                ? config.getAnnotationIntrospector() : null;
-        Class<?> raw = type.getRawClass();
-        return new AnnotatedClass(type, raw, type.getBindings(),
-                ClassUtil.findSuperTypes(type, null, false), intr,
-                (MixInResolver) config, config.getTypeFactory());
+        return construct(type, config, (MixInResolver) config);
     }
 
     /**
-     * @since 2.7
+     * @deprecated Since 2.9, use methods in {@link AnnotatedClassResolver} instead.
      */
+    @Deprecated
     public static AnnotatedClass construct(JavaType type, MapperConfig<?> config,
             MixInResolver mir)
     {
-        AnnotationIntrospector intr = config.isAnnotationProcessingEnabled()
-                ? config.getAnnotationIntrospector() : null;
-        Class<?> raw = type.getRawClass();
-        return new AnnotatedClass(type, raw, type.getBindings(),
-                ClassUtil.findSuperTypes(type, null, false),
-                intr, mir, config.getTypeFactory());
+        return AnnotatedClassResolver.resolve(config, type, mir);
     }
-    
+
     /**
      * Method similar to {@link #construct}, but that will NOT include
      * information from supertypes; only class itself and any direct
      * mix-ins it may have.
      */
-    public static AnnotatedClass constructWithoutSuperTypes(Class<?> cls, MapperConfig<?> config)
-    {
-        if (config == null) {
-            return new AnnotatedClass(null, cls, TypeBindings.emptyBindings(),
-                    Collections.<JavaType>emptyList(), null, null, null);
-        }
-        AnnotationIntrospector intr = config.isAnnotationProcessingEnabled()
-                ? config.getAnnotationIntrospector() : null;
-        return new AnnotatedClass(null, cls, TypeBindings.emptyBindings(),
-                Collections.<JavaType>emptyList(), intr, (MixInResolver) config, config.getTypeFactory());
+    /**
+     * @deprecated Since 2.9, use methods in {@link AnnotatedClassResolver} instead.
+     */
+    @Deprecated
+    public static AnnotatedClass constructWithoutSuperTypes(Class<?> raw, MapperConfig<?> config) {
+        return constructWithoutSuperTypes(raw, config, config);
     }
 
-    public static AnnotatedClass constructWithoutSuperTypes(Class<?> cls, MapperConfig<?> config,
+    /**
+     * @deprecated Since 2.9, use methods in {@link AnnotatedClassResolver} instead.
+     */
+    @Deprecated
+    public static AnnotatedClass constructWithoutSuperTypes(Class<?> raw, MapperConfig<?> config,
             MixInResolver mir)
     {
-        if (config == null) {
-            return new AnnotatedClass(null, cls, TypeBindings.emptyBindings(),
-                    Collections.<JavaType>emptyList(), null, null, null);
-        }
-        AnnotationIntrospector intr = config.isAnnotationProcessingEnabled()
-                ? config.getAnnotationIntrospector() : null;
-        return new AnnotatedClass(null, cls, TypeBindings.emptyBindings(),
-                Collections.<JavaType>emptyList(), intr, mir, config.getTypeFactory());
+        return AnnotatedClassResolver.resolveWithoutSuperTypes(config, raw, mir);
     }
 
     /*
@@ -282,13 +248,15 @@
     }
 
     @Override
+    @Deprecated
     public Iterable<Annotation> annotations() {
-        return _classAnnotations.annotations();
-    }
-    
-    @Override
-    protected AnnotationMap getAllAnnotations() {
-        return _classAnnotations;
+        if (_classAnnotations instanceof AnnotationMap) {
+            return ((AnnotationMap) _classAnnotations).annotations();
+        } else if (_classAnnotations instanceof AnnotationCollector.OneAnnotation ||
+           _classAnnotations instanceof AnnotationCollector.TwoAnnotations) {
+            throw new UnsupportedOperationException("please use getAnnotations/ hasAnnotation to check for Annotations");
+        }
+        return Collections.emptyList();
     }
 
     @Override
@@ -310,67 +278,47 @@
         return _classAnnotations.size() > 0;
     }
 
-    public AnnotatedConstructor getDefaultConstructor()
-    {
-        if (!_creatorsResolved) {
-            resolveCreators();
-        }
-        return _defaultConstructor;
+    public AnnotatedConstructor getDefaultConstructor() {
+        return _creators().defaultConstructor;
     }
 
-    public List<AnnotatedConstructor> getConstructors()
-    {
-        if (!_creatorsResolved) {
-            resolveCreators();
-        }
-        return _constructors;
+    public List<AnnotatedConstructor> getConstructors() {
+        return _creators().constructors;
     }
 
-    public List<AnnotatedMethod> getStaticMethods()
-    {
-        if (!_creatorsResolved) {
-            resolveCreators();
-        }
-        return _creatorMethods;
+    /**
+     * @since 2.9
+     */
+    public List<AnnotatedMethod> getFactoryMethods() {
+        return _creators().creatorMethods;
     }
 
-    public Iterable<AnnotatedMethod> memberMethods()
-    {
-        if (_memberMethods == null) {
-            resolveMemberMethods();
-        }
-        return _memberMethods;
+    /**
+     * @deprecated Since 2.9; use {@link #getFactoryMethods} instead.
+     */
+    @Deprecated
+    public List<AnnotatedMethod> getStaticMethods() {
+        return getFactoryMethods();
     }
 
-    public int getMemberMethodCount()
-    {
-        if (_memberMethods == null) {
-            resolveMemberMethods();
-        }
-        return _memberMethods.size();
+    public Iterable<AnnotatedMethod> memberMethods() {
+        return _methods();
     }
 
-    public AnnotatedMethod findMethod(String name, Class<?>[] paramTypes)
-    {
-        if (_memberMethods == null) {
-            resolveMemberMethods();
-        }
-        return _memberMethods.find(name, paramTypes);
+    public int getMemberMethodCount() {
+        return _methods().size();
+    }
+
+    public AnnotatedMethod findMethod(String name, Class<?>[] paramTypes) {
+        return _methods().find(name, paramTypes);
     }
 
     public int getFieldCount() {
-        if (_fields == null) {
-            resolveFields();
-        }
-        return _fields.size();
+        return _fields().size();
     }
 
-    public Iterable<AnnotatedField> fields()
-    {
-        if (_fields == null) {
-            resolveFields();
-        }
-        return _fields;
+    public Iterable<AnnotatedField> fields() {
+        return _fields();
     }
 
     /**
@@ -387,836 +335,60 @@
 
     /*
     /**********************************************************
-    /* Public API, main-level resolution methods
+    /* Lazily-operating accessors
     /**********************************************************
      */
 
-    /**
-     * Initialization method that will recursively collect Jackson
-     * annotations for this class and all super classes and
-     * interfaces.
-     */
-    private AnnotationMap _resolveClassAnnotations()
-    {
-        AnnotationMap ca = new AnnotationMap();
-        // Should skip processing if annotation processing disabled
-        if (_annotationIntrospector != null) {
-            // add mix-in annotations first (overrides)
-            if (_primaryMixIn != null) {
-                _addClassMixIns(ca, _class, _primaryMixIn);
-            }
-            // first, annotations from the class itself:
-            _addAnnotationsIfNotPresent(ca,
-                    ClassUtil.findClassAnnotations(_class));
-    
-            // and then from super types
-            for (JavaType type : _superTypes) {
-                // and mix mix-in annotations in-between
-                _addClassMixIns(ca, type);
-                _addAnnotationsIfNotPresent(ca,
-                        ClassUtil.findClassAnnotations(type.getRawClass()));
-            }
-            /* and finally... any annotations there might be for plain
-             * old Object.class: separate because for all other purposes
-             * it is just ignored (not included in super types)
-             */
-            /* 12-Jul-2009, tatu: Should this be done for interfaces too?
-             *   For now, yes, seems useful for some cases, and not harmful for any?
-             */
-            _addClassMixIns(ca, Object.class);
-        }
-        return ca;
-    }
-
-    /**
-     * Initialization method that will find out all constructors
-     * and potential static factory methods the class has.
-     */
-    private void resolveCreators()
-    {
-        // Constructor also always members of this class
-        TypeResolutionContext typeContext = this; 
-
-    // 30-Apr-2016, tatu: [databind#1215]: Actually, while true, this does
-    //   NOT apply to context since sub-class may have type bindings
-//    TypeResolutionContext typeContext = new TypeResolutionContext.Basic(_typeFactory, _type.getBindings());
-
-        // Then see which constructors we have
-        List<AnnotatedConstructor> constructors = null;
-
-        // 18-Jun-2016, tatu: Enum constructors will never be useful (unlike
-        //    possibly static factory methods); but they can be royal PITA
-        //    due to some oddities by JVM; see:
-        //    [https://github.com/FasterXML/jackson-module-parameter-names/issues/35]
-        //    for more. So, let's just skip them.
-        if (!_type.isEnumType()) {
-            ClassUtil.Ctor[] declaredCtors = ClassUtil.getConstructors(_class);
-            for (ClassUtil.Ctor ctor : declaredCtors) {
-                if (_isIncludableConstructor(ctor.getConstructor())) {
-                    if (ctor.getParamCount() == 0) {
-                        _defaultConstructor = _constructDefaultConstructor(ctor, typeContext);
-                    } else {
-                        if (constructors == null) {
-                            constructors = new ArrayList<AnnotatedConstructor>(Math.max(10, declaredCtors.length));
-                        }
-                        constructors.add(_constructNonDefaultConstructor(ctor, typeContext));
-                    }
-                }
-            }
-        }
-        if (constructors == null) {
-            _constructors = Collections.emptyList();
-        } else {
-            _constructors = constructors;
-        }
-        // and if need be, augment with mix-ins
-        if (_primaryMixIn != null) {
-            if (_defaultConstructor != null || !_constructors.isEmpty()) {
-                _addConstructorMixIns(_primaryMixIn);
-            }
-        }
-
-        /* And then... let's remove all constructors that are deemed
-         * ignorable after all annotations have been properly collapsed.
-         */
-        // AnnotationIntrospector is null if annotations not enabled; if so, can skip:
-        if (_annotationIntrospector != null) {
-            if (_defaultConstructor != null) {
-                if (_annotationIntrospector.hasIgnoreMarker(_defaultConstructor)) {
-                    _defaultConstructor = null;
-                }
-            }
-            if (_constructors != null) {
-                // count down to allow safe removal
-                for (int i = _constructors.size(); --i >= 0; ) {
-                    if (_annotationIntrospector.hasIgnoreMarker(_constructors.get(i))) {
-                        _constructors.remove(i);
-                    }
-                }
-            }
-        }
-        List<AnnotatedMethod> creatorMethods = null;
-        
-        // Then static methods which are potential factory methods
-        for (Method m : _findClassMethods(_class)) {
-            if (!Modifier.isStatic(m.getModifiers())) {
-                continue;
-            }
-            // all factory methods are fine:
-            //int argCount = m.getParameterTypes().length;
-            if (creatorMethods == null) {
-                creatorMethods = new ArrayList<AnnotatedMethod>(8);
-            }
-            creatorMethods.add(_constructCreatorMethod(m, typeContext));
-        }
-        if (creatorMethods == null) {
-            _creatorMethods = Collections.emptyList();
-        } else {
-            _creatorMethods = creatorMethods;
-            // mix-ins to mix in?
-            if (_primaryMixIn != null) {
-                _addFactoryMixIns(_primaryMixIn);
-            }
-            // anything to ignore at this point?
-            if (_annotationIntrospector != null) {
-                // count down to allow safe removal
-                for (int i = _creatorMethods.size(); --i >= 0; ) {
-                    if (_annotationIntrospector.hasIgnoreMarker(_creatorMethods.get(i))) {
-                        _creatorMethods.remove(i);
-                    }
-                }
-            }
-        }
-        _creatorsResolved = true;
-    }
-
-    /**
-     * Method for resolving member method information: aggregating all non-static methods
-     * and combining annotations (to implement method-annotation inheritance)
-     * 
-     * @param methodFilter Filter used to determine which methods to include
-     */
-    private void resolveMemberMethods()
-    {
-        _memberMethods = _resolveMemberMethods();
-    }
-
-    private AnnotatedMethodMap _resolveMemberMethods()
-    {
-        AnnotatedMethodMap memberMethods = new AnnotatedMethodMap();
-        AnnotatedMethodMap mixins = new AnnotatedMethodMap();
-        // first: methods from the class itself
-        _addMemberMethods(_class, this, memberMethods, _primaryMixIn, mixins);
-
-        // and then augment these with annotations from super-types:
-        for (JavaType type : _superTypes) {
-            Class<?> mixin = (_mixInResolver == null) ? null : _mixInResolver.findMixInClassFor(type.getRawClass());
-            _addMemberMethods(type.getRawClass(),
-                    new TypeResolutionContext.Basic(_typeFactory, type.getBindings()),
-                    memberMethods, mixin, mixins);
-        }
-        // Special case: mix-ins for Object.class? (to apply to ALL classes)
-        if (_mixInResolver != null) {
-            Class<?> mixin = _mixInResolver.findMixInClassFor(Object.class);
-            if (mixin != null) {
-                _addMethodMixIns(_class, memberMethods, mixin, mixins);
-            }
-        }
-
-        /* Any unmatched mix-ins? Most likely error cases (not matching
-         * any method); but there is one possible real use case:
-         * exposing Object#hashCode (alas, Object#getClass can NOT be
-         * exposed)
-         */
-        // 14-Feb-2011, tatu: AnnotationIntrospector is null if annotations not enabled; if so, can skip:
-        if (_annotationIntrospector != null) {
-            if (!mixins.isEmpty()) {
-                Iterator<AnnotatedMethod> it = mixins.iterator();
-                while (it.hasNext()) {
-                    AnnotatedMethod mixIn = it.next();
-                    try {
-                        Method m = Object.class.getDeclaredMethod(mixIn.getName(), mixIn.getRawParameterTypes());
-                        if (m != null) {
-                            // Since it's from java.lang.Object, no generics, no need for real type context:
-                            AnnotatedMethod am = _constructMethod(m, this);
-                            _addMixOvers(mixIn.getAnnotated(), am, false);
-                            memberMethods.add(am);
-                        }
-                    } catch (Exception e) { }
-                }
-            }
-        }
-        return memberMethods;
-    }
-
-    /**
-     * Method that will collect all member (non-static) fields
-     * that are either public, or have at least a single annotation
-     * associated with them.
-     */
-    private void resolveFields()
-    {
-        Map<String,AnnotatedField> foundFields = _findFields(_type, this, null);
-        List<AnnotatedField> f;
-        if (foundFields == null || foundFields.size() == 0) {
-            f = Collections.emptyList();
-        } else {
-            f = new ArrayList<AnnotatedField>(foundFields.size());
-            f.addAll(foundFields.values());
-        }
-        _fields = f;
-    }
-
-    /*
-    /**********************************************************
-    /* Helper methods for resolving class annotations
-    /* (resolution consisting of inheritance, overrides,
-    /* and injection of mix-ins as necessary)
-    /**********************************************************
-     */
-    
-    /**
-     * Helper method for adding any mix-in annotations specified
-     * class might have.
-     */
-    protected void _addClassMixIns(AnnotationMap annotations, JavaType target)
-    {
-        if (_mixInResolver != null) {
-            final Class<?> toMask = target.getRawClass();
-            _addClassMixIns(annotations, toMask, _mixInResolver.findMixInClassFor(toMask));
-        }
-    }
-
-    protected void _addClassMixIns(AnnotationMap annotations, Class<?> target)
-    {
-        if (_mixInResolver != null) {
-            _addClassMixIns(annotations, target, _mixInResolver.findMixInClassFor(target));
-        }
-    }
-
-    protected void _addClassMixIns(AnnotationMap annotations, Class<?> toMask,
-            Class<?> mixin)
-    {
-        if (mixin == null) {
-            return;
-        }
-        // Ok, first: annotations from mix-in class itself:
-        _addAnnotationsIfNotPresent(annotations, ClassUtil.findClassAnnotations(mixin));
-
-        /* And then from its supertypes, if any. But note that we will
-         * only consider super-types up until reaching the masked
-         * class (if found); this because often mix-in class
-         * is a sub-class (for convenience reasons). And if so, we
-         * absolutely must NOT include super types of masked class,
-         * as that would inverse precedence of annotations.
-         */
-        for (Class<?> parent : ClassUtil.findSuperClasses(mixin, toMask, false)) {
-            _addAnnotationsIfNotPresent(annotations, ClassUtil.findClassAnnotations(parent));
-        }
-    }
-
-    /*
-    /**********************************************************
-    /* Helper methods for populating creator (ctor, factory) information
-    /**********************************************************
-     */
-
-    protected void _addConstructorMixIns(Class<?> mixin)
-    {
-        MemberKey[] ctorKeys = null;
-        int ctorCount = (_constructors == null) ? 0 : _constructors.size();
-        for (ClassUtil.Ctor ctor0 : ClassUtil.getConstructors(mixin)) {
-            Constructor<?> ctor = ctor0.getConstructor();
-            if (ctor.getParameterTypes().length == 0) {
-                if (_defaultConstructor != null) {
-                    _addMixOvers(ctor, _defaultConstructor, false);
-                }
+    private final List<AnnotatedField> _fields() {
+        List<AnnotatedField> f = _fields;
+        if (f == null) {
+            // 09-Jun-2017, tatu: _type only null for primordial, placeholder array types.
+            if (_type == null) {
+                f = Collections.emptyList();
             } else {
-                if (ctorKeys == null) {
-                    ctorKeys = new MemberKey[ctorCount];
-                    for (int i = 0; i < ctorCount; ++i) {
-                        ctorKeys[i] = new MemberKey(_constructors.get(i).getAnnotated());
-                    }
-                }
-                MemberKey key = new MemberKey(ctor);
-
-                for (int i = 0; i < ctorCount; ++i) {
-                    if (!key.equals(ctorKeys[i])) {
-                        continue;
-                    }
-                    _addMixOvers(ctor, _constructors.get(i), true);
-                    break;
-                }
+                f = AnnotatedFieldCollector.collectFields(_annotationIntrospector,
+                        this, _mixInResolver, _typeFactory, _type);
             }
+            _fields = f;
         }
+        return f;
     }
 
-    protected void _addFactoryMixIns(Class<?> mixin)
-    {
-        MemberKey[] methodKeys = null;
-        int methodCount = _creatorMethods.size();
-
-        for (Method m : ClassUtil.getDeclaredMethods(mixin)) {
-            if (!Modifier.isStatic(m.getModifiers())) {
-                continue;
-            }
-            if (m.getParameterTypes().length == 0) {
-                continue;
-            }
-            if (methodKeys == null) {
-                methodKeys = new MemberKey[methodCount];
-                for (int i = 0; i < methodCount; ++i) {
-                    methodKeys[i] = new MemberKey(_creatorMethods.get(i).getAnnotated());
-                }
-            }
-            MemberKey key = new MemberKey(m);
-            for (int i = 0; i < methodCount; ++i) {
-                if (!key.equals(methodKeys[i])) {
-                    continue;
-                }
-                _addMixOvers(m, _creatorMethods.get(i), true);
-                break;
-            }
-        }
-    }
-
-    /*
-    /**********************************************************
-    /* Helper methods for populating method information
-    /**********************************************************
-     */
-
-    protected void _addMemberMethods(Class<?> cls, TypeResolutionContext typeContext,
-            AnnotatedMethodMap methods,
-            Class<?> mixInCls, AnnotatedMethodMap mixIns)
-    {
-        // first, mixIns, since they have higher priority then class methods
-        if (mixInCls != null) {
-            _addMethodMixIns(cls, methods, mixInCls, mixIns);
-        }
-        if (cls == null) { // just so caller need not check when passing super-class
-            return;
-        }
-        // then methods from the class itself
-        for (Method m : _findClassMethods(cls)) {
-            if (!_isIncludableMemberMethod(m)) {
-                continue;
-            }
-            AnnotatedMethod old = methods.find(m);
-            if (old == null) {
-                AnnotatedMethod newM = _constructMethod(m, typeContext);
-                methods.add(newM);
-                // Ok, but is there a mix-in to connect now?
-                old = mixIns.remove(m);
-                if (old != null) {
-                    _addMixOvers(old.getAnnotated(), newM, false);
-                }
+    private final AnnotatedMethodMap _methods() {
+        AnnotatedMethodMap m = _memberMethods;
+        if (m == null) {
+            // 09-Jun-2017, tatu: _type only null for primordial, placeholder array types.
+            //    NOTE: would be great to have light-weight shareable maps; no such impl exists for now
+            if (_type == null) {
+                m = new AnnotatedMethodMap();
             } else {
-                /* If sub-class already has the method, we only want to augment
-                 * annotations with entries that are not masked by sub-class.
-                 */
-                _addMixUnders(m, old);
-
-                /* 06-Jan-2010, tatu: [JACKSON-450] Except that if method we saw first is
-                 *   from an interface, and we now find a non-interface definition, we should
-                 *   use this method, but with combination of annotations.
-                 *   This helps (or rather, is essential) with JAXB annotations and
-                 *   may also result in faster method calls (interface calls are slightly
-                 *   costlier than regular method calls)
-                 */
-                if (old.getDeclaringClass().isInterface() && !m.getDeclaringClass().isInterface()) {
-                    methods.add(old.withMethod(m));
-                }
+                m = AnnotatedMethodCollector.collectMethods(_annotationIntrospector,
+                        this,
+                        _mixInResolver, _typeFactory,
+                        _type, _superTypes, _primaryMixIn);
             }
+            _memberMethods = m;
         }
+        return m;
     }
 
-    protected void _addMethodMixIns(Class<?> targetClass, AnnotatedMethodMap methods,
-            Class<?> mixInCls, AnnotatedMethodMap mixIns)
-    {
-//        List<Class<?>> parents = ClassUtil.findSuperClasses(mixInCls, targetClass, true);
-
-        List<Class<?>> parents = ClassUtil.findRawSuperTypes(mixInCls, targetClass, true);
-        for (Class<?> mixin : parents) {
-            for (Method m : ClassUtil.getDeclaredMethods(mixin)) {
-                if (!_isIncludableMemberMethod(m)) {
-                    continue;
-                }
-                AnnotatedMethod am = methods.find(m);
-                /* Do we already have a method to augment (from sub-class
-                 * that will mask this mixIn)? If so, add if visible
-                 * without masking (no such annotation)
-                 */
-                if (am != null) {
-                    _addMixUnders(m, am);
-                    /* Otherwise will have precedence, but must wait
-                     * until we find the real method (mixIn methods are
-                     * just placeholder, can't be called)
-                     */
-                } else {
-                    // Well, or, as per [databind#515], multi-level merge within mixins...
-                    am = mixIns.find(m);
-                    if (am != null) {
-                        _addMixUnders(m, am);
-                    } else {
-                        // 03-Nov-2015, tatu: Mix-in method never called, should not need
-                        //    to resolve generic types, so this class is fine as context
-                        mixIns.add(_constructMethod(m, this));
-                    }
-                }
+    private final Creators _creators() {
+        Creators c = _creators;
+        if (c == null) {
+            if (_type == null) {
+                c = NO_CREATORS;
+            } else {
+                c = AnnotatedCreatorCollector.collectCreators(_annotationIntrospector,
+                        this, _type, _primaryMixIn);
             }
+            _creators = c;
         }
+        return c;
     }
 
     /*
     /**********************************************************
-    /* Helper methods for populating field information
-    /**********************************************************
-     */
-
-    protected Map<String,AnnotatedField> _findFields(JavaType type,
-            TypeResolutionContext typeContext, Map<String,AnnotatedField> fields)
-    {
-        /* First, a quick test: we only care for regular classes (not
-         * interfaces, primitive types etc), except for Object.class.
-         * A simple check to rule out other cases is to see if there
-         * is a super class or not.
-         */
-        JavaType parent = type.getSuperClass();
-        if (parent != null) {
-            final Class<?> cls = type.getRawClass();
-            // Let's add super-class' fields first, then ours.
-            /* 21-Feb-2010, tatu: Need to handle masking: as per [JACKSON-226]
-             *    we otherwise get into trouble...
-             */
-            fields = _findFields(parent,
-                    new TypeResolutionContext.Basic(_typeFactory, parent.getBindings()),
-                    fields);
-            for (Field f : ClassUtil.getDeclaredFields(cls)) {
-                // static fields not included (transients are at this point, filtered out later)
-                if (!_isIncludableField(f)) {
-                    continue;
-                }
-                /* Ok now: we can (and need) not filter out ignorable fields
-                 * at this point; partly because mix-ins haven't been
-                 * added, and partly because logic can be done when
-                 * determining get/settability of the field.
-                 */
-                if (fields == null) {
-                    fields = new LinkedHashMap<String,AnnotatedField>();
-                }
-                fields.put(f.getName(), _constructField(f, typeContext));
-            }
-            // And then... any mix-in overrides?
-            if (_mixInResolver != null) {
-                Class<?> mixin = _mixInResolver.findMixInClassFor(cls);
-                if (mixin != null) {
-                    _addFieldMixIns(mixin, cls, fields);
-                }
-            }
-        }
-        return fields;
-    }
-
-    /**
-     * Method called to add field mix-ins from given mix-in class (and its fields)
-     * into already collected actual fields (from introspected classes and their
-     * super-classes)
-     */
-    protected void _addFieldMixIns(Class<?> mixInCls, Class<?> targetClass,
-            Map<String,AnnotatedField> fields)
-    {
-        List<Class<?>> parents = ClassUtil.findSuperClasses(mixInCls, targetClass, true);
-        for (Class<?> mixin : parents) {
-            for (Field mixinField : ClassUtil.getDeclaredFields(mixin)) {
-                // there are some dummy things (static, synthetic); better ignore
-                if (!_isIncludableField(mixinField)) {
-                    continue;
-                }
-                String name = mixinField.getName();
-                // anything to mask? (if not, quietly ignore)
-                AnnotatedField maskedField = fields.get(name);
-                if (maskedField != null) {
-                    _addOrOverrideAnnotations(maskedField, mixinField.getDeclaredAnnotations());
-                }
-            }
-        }
-    }
-
-    /*
-    /**********************************************************
-    /* Helper methods, constructing value types
-    /**********************************************************
-     */
-
-    protected AnnotatedMethod _constructMethod(Method m, TypeResolutionContext typeContext)
-    {
-        /* note: parameter annotations not used for regular (getter, setter)
-         * methods; only for creator methods (static factory methods)
-         * -- at least not yet!
-         */
-        if (_annotationIntrospector == null) { // when annotation processing is disabled
-            return new AnnotatedMethod(typeContext, m, _emptyAnnotationMap(), null);
-        }
-        return new AnnotatedMethod(typeContext, m, _collectRelevantAnnotations(m.getDeclaredAnnotations()), null);
-    }
-
-    protected AnnotatedConstructor _constructDefaultConstructor(ClassUtil.Ctor ctor,
-            TypeResolutionContext typeContext)
-    {
-        if (_annotationIntrospector == null) { // when annotation processing is disabled
-            return new AnnotatedConstructor(typeContext, ctor.getConstructor(), _emptyAnnotationMap(), NO_ANNOTATION_MAPS);
-        }
-        return new AnnotatedConstructor(typeContext, ctor.getConstructor(),
-                _collectRelevantAnnotations(ctor.getDeclaredAnnotations()), NO_ANNOTATION_MAPS);
-    }
-
-    protected AnnotatedConstructor _constructNonDefaultConstructor(ClassUtil.Ctor ctor,
-            TypeResolutionContext typeContext)
-    {
-        final int paramCount = ctor.getParamCount();
-        if (_annotationIntrospector == null) { // when annotation processing is disabled
-            return new AnnotatedConstructor(typeContext, ctor.getConstructor(),
-                    _emptyAnnotationMap(), _emptyAnnotationMaps(paramCount));
-        }
-
-        /* Looks like JDK has discrepancy, whereas annotations for implicit 'this'
-         * (for non-static inner classes) are NOT included, but type is?
-         * Strange, sounds like a bug. Alas, we can't really fix that...
-         */
-        if (paramCount == 0) { // no-arg default constructors, can simplify slightly
-            return new AnnotatedConstructor(typeContext, ctor.getConstructor(),
-                    _collectRelevantAnnotations(ctor.getDeclaredAnnotations()), NO_ANNOTATION_MAPS);
-        }
-        // Also: enum value constructors
-        AnnotationMap[] resolvedAnnotations;
-        Annotation[][] paramAnns = ctor.getParameterAnnotations();
-        if (paramCount != paramAnns.length) {
-            // Limits of the work-around (to avoid hiding real errors):
-            // first, only applicable for member classes and then either:
-
-            resolvedAnnotations = null;
-            Class<?> dc = ctor.getDeclaringClass();
-            // (a) is enum, which have two extra hidden params (name, index)
-            if (dc.isEnum() && (paramCount == paramAnns.length + 2)) {
-                Annotation[][] old = paramAnns;
-                paramAnns = new Annotation[old.length+2][];
-                System.arraycopy(old, 0, paramAnns, 2, old.length);
-                resolvedAnnotations = _collectRelevantAnnotations(paramAnns);
-            } else if (dc.isMemberClass()) {
-                // (b) non-static inner classes, get implicit 'this' for parameter, not  annotation
-                if (paramCount == (paramAnns.length + 1)) {
-                    // hack attack: prepend a null entry to make things match
-                    Annotation[][] old = paramAnns;
-                    paramAnns = new Annotation[old.length+1][];
-                    System.arraycopy(old, 0, paramAnns, 1, old.length);
-                    resolvedAnnotations = _collectRelevantAnnotations(paramAnns);
-                }
-            }
-            if (resolvedAnnotations == null) {
-                throw new IllegalStateException("Internal error: constructor for "+ctor.getDeclaringClass().getName()
-                        +" has mismatch: "+paramCount+" parameters; "+paramAnns.length+" sets of annotations");
-            }
-        } else {
-            resolvedAnnotations = _collectRelevantAnnotations(paramAnns);
-        }
-        return new AnnotatedConstructor(typeContext, ctor.getConstructor(),
-                _collectRelevantAnnotations(ctor.getDeclaredAnnotations()), resolvedAnnotations);
-    }
-
-    protected AnnotatedMethod _constructCreatorMethod(Method m, TypeResolutionContext typeContext)
-    {
-        final int paramCount = m.getParameterTypes().length;
-        if (_annotationIntrospector == null) { // when annotation processing is disabled
-            return new AnnotatedMethod(typeContext, m, _emptyAnnotationMap(), _emptyAnnotationMaps(paramCount));
-        }
-        if (paramCount == 0) { // common enough we can slightly optimize
-            return new AnnotatedMethod(typeContext, m, _collectRelevantAnnotations(m.getDeclaredAnnotations()),
-                    NO_ANNOTATION_MAPS);
-        }
-        return new AnnotatedMethod(typeContext, m, _collectRelevantAnnotations(m.getDeclaredAnnotations()),
-                                   _collectRelevantAnnotations(m.getParameterAnnotations()));
-    }
-
-    protected AnnotatedField _constructField(Field f, TypeResolutionContext typeContext)
-    {
-        if (_annotationIntrospector == null) { // when annotation processing is disabled
-            return new AnnotatedField(typeContext, f, _emptyAnnotationMap());
-        }
-        return new AnnotatedField(typeContext, f, _collectRelevantAnnotations(f.getDeclaredAnnotations()));
-    }
- 
-    private AnnotationMap _emptyAnnotationMap() {
-        return new AnnotationMap();
-    }
-
-    private AnnotationMap[] _emptyAnnotationMaps(int count) {
-        if (count == 0) {
-            return NO_ANNOTATION_MAPS;
-        }
-        AnnotationMap[] maps = new AnnotationMap[count];
-        for (int i = 0; i < count; ++i) {
-            maps[i] = _emptyAnnotationMap();
-        }
-        return maps;
-    }
-    
-    /*
-    /**********************************************************
-    /* Helper methods, inclusion filtering
-    /**********************************************************
-     */
-
-    protected boolean _isIncludableMemberMethod(Method m)
-    {
-        if (Modifier.isStatic(m.getModifiers())) {
-            return false;
-        }
-        /* 07-Apr-2009, tatu: Looks like generics can introduce hidden
-         *   bridge and/or synthetic methods. I don't think we want to
-         *   consider those...
-         */
-        if (m.isSynthetic() || m.isBridge()) {
-            return false;
-        }
-        // also, for now we have no use for methods with 2 or more arguments:
-        int pcount = m.getParameterTypes().length;
-        return (pcount <= 2);
-    }
-
-    private boolean _isIncludableField(Field f)
-    {
-        // Most likely synthetic fields, if any, are to be skipped similar to methods
-        if (f.isSynthetic()) {
-            return false;
-        }
-        // Static fields are never included. Transient are (since 2.6), for
-        // purpose of propagating removal
-        int mods = f.getModifiers();
-        if (Modifier.isStatic(mods)) {
-            return false;
-        }
-        return true;
-    }
-
-    // for [databind#1005]: do not use or expose synthetic constructors
-    private boolean _isIncludableConstructor(Constructor<?> c)
-    {
-        return !c.isSynthetic();
-    }
-
-    /*
-    /**********************************************************
-    /* Helper methods, attaching annotations
-    /**********************************************************
-     */
-
-    protected AnnotationMap[] _collectRelevantAnnotations(Annotation[][] anns)
-    {
-        int len = anns.length;
-        AnnotationMap[] result = new AnnotationMap[len];
-        for (int i = 0; i < len; ++i) {
-            result[i] = _collectRelevantAnnotations(anns[i]);
-        }
-        return result;
-    }
-
-    protected AnnotationMap _collectRelevantAnnotations(Annotation[] anns)
-    {
-        return _addAnnotationsIfNotPresent(new AnnotationMap(), anns);
-    }
-    
-    /* Helper method used to add all applicable annotations from given set.
-     * Takes into account possible "annotation bundles" (meta-annotations to
-     * include instead of main-level annotation)
-     */
-    private AnnotationMap _addAnnotationsIfNotPresent(AnnotationMap result, Annotation[] anns)
-    {
-        if (anns != null) {
-            List<Annotation> fromBundles = null;
-            for (Annotation ann : anns) { // first: direct annotations
-                // note: we will NOT filter out non-Jackson anns any more
-                boolean wasNotPresent = result.addIfNotPresent(ann);
-                if (wasNotPresent && _isAnnotationBundle(ann)) {
-                    fromBundles = _addFromBundle(ann, fromBundles);
-                }
-            }
-            if (fromBundles != null) { // and secondarily handle bundles, if any found: precedence important
-                _addAnnotationsIfNotPresent(result, fromBundles.toArray(new Annotation[fromBundles.size()]));
-            }
-        }
-        return result;
-    }
-
-    private List<Annotation> _addFromBundle(Annotation bundle, List<Annotation> result)
-    {
-        for (Annotation a : ClassUtil.findClassAnnotations(bundle.annotationType())) {
-            // minor optimization: by-pass 2 common JDK meta-annotations
-            if ((a instanceof Target) || (a instanceof Retention)) {
-                continue;
-            }
-            if (result == null) {
-                result = new ArrayList<Annotation>();
-            }
-            result.add(a);
-        }
-        return result;
-    }
-    
-    private void _addAnnotationsIfNotPresent(AnnotatedMember target, Annotation[] anns)
-    {
-        if (anns != null) {
-            List<Annotation> fromBundles = null;
-            for (Annotation ann : anns) { // first: direct annotations
-                boolean wasNotPresent = target.addIfNotPresent(ann);
-                if (wasNotPresent && _isAnnotationBundle(ann)) {
-                    fromBundles = _addFromBundle(ann, fromBundles);
-                }
-            }
-            if (fromBundles != null) { // and secondarily handle bundles, if any found: precedence important
-                _addAnnotationsIfNotPresent(target, fromBundles.toArray(new Annotation[fromBundles.size()]));
-            }
-        }
-    }
-    
-    private void _addOrOverrideAnnotations(AnnotatedMember target, Annotation[] anns)
-    {
-        if (anns != null) {
-            List<Annotation> fromBundles = null;
-            for (Annotation ann : anns) { // first: direct annotations
-                boolean wasModified = target.addOrOverride(ann);
-                if (wasModified && _isAnnotationBundle(ann)) {
-                    fromBundles = _addFromBundle(ann, fromBundles);
-                }
-            }
-            if (fromBundles != null) { // and then bundles, if any: important for precedence
-                _addOrOverrideAnnotations(target, fromBundles.toArray(new Annotation[fromBundles.size()]));
-            }
-        }
-    }
-    
-    /**
-     * @param addParamAnnotations Whether parameter annotations are to be
-     *   added as well
-     */
-    protected void _addMixOvers(Constructor<?> mixin, AnnotatedConstructor target,
-            boolean addParamAnnotations)
-    {
-        _addOrOverrideAnnotations(target, mixin.getDeclaredAnnotations());
-        if (addParamAnnotations) {
-            Annotation[][] pa = mixin.getParameterAnnotations();
-            for (int i = 0, len = pa.length; i < len; ++i) {
-                for (Annotation a : pa[i]) {
-                    target.addOrOverrideParam(i, a);
-                }
-            }
-        }
-    }
-
-    /**
-     * @param addParamAnnotations Whether parameter annotations are to be
-     *   added as well
-     */
-    protected void _addMixOvers(Method mixin, AnnotatedMethod target,
-            boolean addParamAnnotations)
-    {
-        _addOrOverrideAnnotations(target, mixin.getDeclaredAnnotations());
-        if (addParamAnnotations) {
-            Annotation[][] pa = mixin.getParameterAnnotations();
-            for (int i = 0, len = pa.length; i < len; ++i) {
-                for (Annotation a : pa[i]) {
-                    target.addOrOverrideParam(i, a);
-                }
-            }
-        }
-    }
-
-    /**
-     * Method that will add annotations from specified source method to target method,
-     * but only if target does not yet have them.
-     */
-    protected void _addMixUnders(Method src, AnnotatedMethod target) {
-        _addAnnotationsIfNotPresent(target, src.getDeclaredAnnotations());
-    }
-
-    private final boolean _isAnnotationBundle(Annotation ann) {
-        return (_annotationIntrospector != null) && _annotationIntrospector.isAnnotationBundle(ann);
-    }
-
-    /**
-     * Helper method that gets methods declared in given class; usually a simple thing,
-     * but sometimes (as per [databind#785]) more complicated, depending on classloader
-     * setup.
-     *
-     * @since 2.4.7
-     */
-    protected Method[] _findClassMethods(Class<?> cls)
-    {
-        try {
-            return ClassUtil.getDeclaredMethods(cls);
-        } catch (final NoClassDefFoundError ex) {
-            // One of the methods had a class that was not found in the cls.getClassLoader.
-            // Maybe the developer was nice and has a different class loader for this context.
-            final ClassLoader loader = Thread.currentThread().getContextClassLoader();
-            if (loader == null){
-                // Nope... this is going to end poorly
-                throw ex;
-            }
-            final Class<?> contextClass;
-            try {
-                contextClass = loader.loadClass(cls.getName());
-            } catch (ClassNotFoundException e) {
-                // !!! TODO: 08-May-2015, tatu: Chain appropriately once we have JDK 1.7/Java7 as baseline
-                //ex.addSuppressed(e); Not until Jackson 2.7
-               throw ex;
-            }
-            return contextClass.getDeclaredMethods(); // Cross fingers
-        }
-    }
-
-    /*
-    /**********************************************************
-    /* Other methods
+    /* Standard method overrides
     /**********************************************************
      */
 
@@ -1233,7 +405,43 @@
     @Override
     public boolean equals(Object o) {
         if (o == this) return true;
-        if (o == null || o.getClass() != getClass()) return false;
+        if (!ClassUtil.hasClass(o, getClass())) {
+            return false;
+        }
         return ((AnnotatedClass) o)._class == _class;
     }
+
+    /*
+    /**********************************************************
+    /* Helper classes
+    /**********************************************************
+     */
+
+    public static final class Creators
+    {
+        /**
+         * Default constructor of the annotated class, if it has one.
+         */
+        public final AnnotatedConstructor defaultConstructor;
+
+        /**
+         * Single argument constructors the class has, if any.
+         */
+        public final List<AnnotatedConstructor> constructors;
+
+        /**
+         * Single argument static methods that might be usable
+         * as factory methods
+         */
+        public final List<AnnotatedMethod> creatorMethods;
+
+        public Creators(AnnotatedConstructor defCtor,
+                List<AnnotatedConstructor> ctors,
+                List<AnnotatedMethod> ctorMethods)
+        {
+            defaultConstructor = defCtor;
+            constructors = ctors;
+            creatorMethods = ctorMethods;
+        }
+    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClassResolver.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClassResolver.java
new file mode 100644
index 0000000..65326e2
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClassResolver.java
@@ -0,0 +1,233 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.util.Collections;
+import java.util.List;
+
+import com.fasterxml.jackson.databind.AnnotationIntrospector;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.cfg.MapperConfig;
+import com.fasterxml.jackson.databind.introspect.ClassIntrospector.MixInResolver;
+import com.fasterxml.jackson.databind.type.TypeBindings;
+import com.fasterxml.jackson.databind.util.Annotations;
+import com.fasterxml.jackson.databind.util.ClassUtil;
+
+/**
+ * Helper class that contains logic for resolving annotations to construct
+ * {@link AnnotatedClass} instances.
+ *
+ * @since 2.9
+ */
+public class AnnotatedClassResolver
+{
+    private final static Annotations NO_ANNOTATIONS = AnnotationCollector.emptyAnnotations();
+
+    private final MapperConfig<?> _config;
+    private final AnnotationIntrospector _intr;
+    private final MixInResolver _mixInResolver;
+    private final TypeBindings _bindings;
+
+    private final JavaType _type;
+    private final Class<?> _class;
+    private final Class<?> _primaryMixin;
+
+    AnnotatedClassResolver(MapperConfig<?> config, JavaType type, MixInResolver r) {
+        _config = config;
+        _type = type;
+        _class = type.getRawClass();
+        _mixInResolver = r;
+        _bindings = type.getBindings();
+        _intr = config.isAnnotationProcessingEnabled()
+                ? config.getAnnotationIntrospector() : null;
+        _primaryMixin = _config.findMixInClassFor(_class);
+    }
+
+    AnnotatedClassResolver(MapperConfig<?> config, Class<?> cls, MixInResolver r) {
+        _config = config;
+        _type = null;
+        _class = cls;
+        _mixInResolver = r;
+        _bindings = TypeBindings.emptyBindings();
+        if (config == null) {
+            _intr = null;
+            _primaryMixin = null;
+        } else {
+            _intr = config.isAnnotationProcessingEnabled()
+                    ? config.getAnnotationIntrospector() : null;
+            _primaryMixin = _config.findMixInClassFor(_class);
+        }
+    }
+
+    public static AnnotatedClass resolve(MapperConfig<?> config, JavaType forType,
+            MixInResolver r)
+    {
+        if (forType.isArrayType() && skippableArray(config, forType.getRawClass())) {
+            return createArrayType(config, forType.getRawClass());
+        }
+        return new AnnotatedClassResolver(config, forType, r).resolveFully();
+    }
+
+    public static AnnotatedClass resolveWithoutSuperTypes(MapperConfig<?> config, Class<?> forType) {
+        return resolveWithoutSuperTypes(config, forType, config);
+    }
+
+    public static AnnotatedClass resolveWithoutSuperTypes(MapperConfig<?> config, JavaType forType,
+            MixInResolver r)
+    {
+        if (forType.isArrayType() && skippableArray(config, forType.getRawClass())) {
+            return createArrayType(config, forType.getRawClass());
+        }
+        return new AnnotatedClassResolver(config, forType, r).resolveWithoutSuperTypes();
+    }
+
+    public static AnnotatedClass resolveWithoutSuperTypes(MapperConfig<?> config, Class<?> forType,
+            MixInResolver r)
+    {
+        if (forType.isArray() && skippableArray(config, forType)) {
+            return createArrayType(config, forType);
+        }
+        return new AnnotatedClassResolver(config, forType, r).resolveWithoutSuperTypes();
+    }
+
+    private static boolean skippableArray(MapperConfig<?> config, Class<?> type) {
+        return (config == null) || (config.findMixInClassFor(type) == null);
+                
+    }
+
+    /**
+     * Internal helper method used for resolving a small set of "primordial" types for which
+     * we do not accept any annotation information or overrides. 
+     */
+    static AnnotatedClass createPrimordial(Class<?> raw) {
+        return new AnnotatedClass(raw);
+    }
+
+    /**
+     * Internal helper method used for resolving array types, unless they happen
+     * to have associated mix-in to apply.
+     */
+    static AnnotatedClass createArrayType(MapperConfig<?> config, Class<?> raw) {
+        return new AnnotatedClass(raw);
+    }
+
+    AnnotatedClass resolveFully() {
+        List<JavaType> superTypes = ClassUtil.findSuperTypes(_type, null, false);
+        return new AnnotatedClass(_type, _class, superTypes, _primaryMixin,
+                resolveClassAnnotations(superTypes),
+                _bindings, _intr, _mixInResolver, _config.getTypeFactory());
+
+    }
+
+    AnnotatedClass resolveWithoutSuperTypes() {
+        List<JavaType> superTypes = Collections.<JavaType>emptyList();
+        return new AnnotatedClass(null, _class, superTypes, _primaryMixin,
+                resolveClassAnnotations(superTypes),
+                _bindings, _intr, _config, _config.getTypeFactory());
+    }
+
+    /*
+    /**********************************************************
+    /* Class annotation resolution
+    /**********************************************************
+     */
+
+    /**
+     * Initialization method that will recursively collect Jackson
+     * annotations for this class and all super classes and
+     * interfaces.
+     */
+    private Annotations resolveClassAnnotations(List<JavaType> superTypes)
+    {
+        // Should skip processing if annotation processing disabled
+        if (_intr == null) {
+            return NO_ANNOTATIONS;
+        }
+        AnnotationCollector resolvedCA = AnnotationCollector.emptyCollector();
+        // add mix-in annotations first (overrides)
+        if (_primaryMixin != null) {
+            resolvedCA = _addClassMixIns(resolvedCA, _class, _primaryMixin);
+        }
+        // then annotations from the class itself:
+        resolvedCA = _addAnnotationsIfNotPresent(resolvedCA,
+                ClassUtil.findClassAnnotations(_class));
+
+        // and then from super types
+        for (JavaType type : superTypes) {
+            // and mix mix-in annotations in-between
+            if (_mixInResolver != null) {
+                Class<?> cls = type.getRawClass();
+                resolvedCA = _addClassMixIns(resolvedCA, cls,
+                        _mixInResolver.findMixInClassFor(cls));
+            }
+            resolvedCA = _addAnnotationsIfNotPresent(resolvedCA,
+                    ClassUtil.findClassAnnotations(type.getRawClass()));
+        }
+        /* and finally... any annotations there might be for plain
+         * old Object.class: separate because for all other purposes
+         * it is just ignored (not included in super types)
+         */
+        // 12-Jul-2009, tatu: Should this be done for interfaces too?
+        //  For now, yes, seems useful for some cases, and not harmful for any?
+        if (_mixInResolver != null) {
+            resolvedCA = _addClassMixIns(resolvedCA, Object.class,
+                    _mixInResolver.findMixInClassFor(Object.class));
+        }
+        return resolvedCA.asAnnotations();
+    }
+
+    private AnnotationCollector _addClassMixIns(AnnotationCollector annotations,
+            Class<?> target, Class<?> mixin)
+    {
+        if (mixin != null) {
+            // Ok, first: annotations from mix-in class itself:
+            annotations = _addAnnotationsIfNotPresent(annotations, ClassUtil.findClassAnnotations(mixin));
+    
+            // And then from its supertypes, if any. But note that we will only consider
+            // super-types up until reaching the masked class (if found); this because
+            // often mix-in class is a sub-class (for convenience reasons).
+            // And if so, we absolutely must NOT include super types of masked class,
+            // as that would inverse precedence of annotations.
+            for (Class<?> parent : ClassUtil.findSuperClasses(mixin, target, false)) {
+                annotations = _addAnnotationsIfNotPresent(annotations, ClassUtil.findClassAnnotations(parent));
+            }
+        }
+        return annotations;
+    }
+
+    private AnnotationCollector _addAnnotationsIfNotPresent(AnnotationCollector c,
+            Annotation[] anns)
+    {
+        if (anns != null) {
+            for (Annotation ann : anns) { // first: direct annotations
+                // note: we will NOT filter out non-Jackson annotations any more
+                if (!c.isPresent(ann)) {
+                    c = c.addOrOverride(ann);
+                    if (_intr.isAnnotationBundle(ann)) {
+                        c = _addFromBundleIfNotPresent(c, ann);
+                    }
+                }
+            }
+        }
+        return c;
+    }
+
+    private AnnotationCollector _addFromBundleIfNotPresent(AnnotationCollector c,
+            Annotation bundle)
+    {
+        for (Annotation ann : ClassUtil.findClassAnnotations(bundle.annotationType())) {
+            // minor optimization: by-pass 2 common JDK meta-annotations
+            if ((ann instanceof Target) || (ann instanceof Retention)) {
+                continue;
+            }
+            if (!c.isPresent(ann)) {
+                c = c.addOrOverride(ann);
+                if (_intr.isAnnotationBundle(ann)) {
+                    c = _addFromBundleIfNotPresent(c, ann);
+                }
+            }
+        }
+        return c;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedConstructor.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedConstructor.java
index a5e81ad..4e17a00 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedConstructor.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedConstructor.java
@@ -176,10 +176,10 @@
     @Override
     public boolean equals(Object o) {
         if (o == this) return true;
-        if (o == null || o.getClass() != getClass()) return false;
-        return ((AnnotatedConstructor) o)._constructor == _constructor;
+        return ClassUtil.hasClass(o, getClass())
+                && (((AnnotatedConstructor) o)._constructor == _constructor);
     }
-    
+
     /*
     /**********************************************************
     /* JDK serialization handling
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedCreatorCollector.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedCreatorCollector.java
new file mode 100644
index 0000000..0ec6d66
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedCreatorCollector.java
@@ -0,0 +1,358 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import com.fasterxml.jackson.databind.AnnotationIntrospector;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.introspect.AnnotatedClass.Creators;
+import com.fasterxml.jackson.databind.util.ClassUtil;
+
+/**
+ * Helper class used to contain details of how Creators (annotated constructors
+ * and static methods) are discovered to be accessed by and via {@link AnnotatedClass}.
+ *
+ * @since 2.9
+ */
+final class AnnotatedCreatorCollector
+    extends CollectorBase
+{
+    // // // Configuration
+
+    private final TypeResolutionContext _typeContext;
+
+    // // // Collected state
+
+    private AnnotatedConstructor _defaultConstructor;
+
+    AnnotatedCreatorCollector(AnnotationIntrospector intr,
+            TypeResolutionContext tc)
+    {
+        super(intr);
+        _typeContext = tc;
+    }
+
+    public static Creators collectCreators(AnnotationIntrospector intr,
+            TypeResolutionContext tc, 
+            JavaType type, Class<?> primaryMixIn)
+    {
+        // Constructor also always members of resolved class, parent == resolution context
+        return new AnnotatedCreatorCollector(intr, tc)
+                .collect(type, primaryMixIn);
+    }
+
+    Creators collect(JavaType type, Class<?> primaryMixIn)
+    {
+    // 30-Apr-2016, tatu: [databind#1215]: Actually, while true, this does
+    //   NOT apply to context since sub-class may have type bindings
+//        TypeResolutionContext typeContext = new TypeResolutionContext.Basic(_typeFactory, _type.getBindings());
+
+        List<AnnotatedConstructor> constructors = _findPotentialConstructors(type, primaryMixIn);
+        List<AnnotatedMethod> factories = _findPotentialFactories(type, primaryMixIn);
+
+        /* And then... let's remove all constructors that are deemed
+         * ignorable after all annotations have been properly collapsed.
+         */
+        // AnnotationIntrospector is null if annotations not enabled; if so, can skip:
+        if (_intr != null) {
+            if (_defaultConstructor != null) {
+                if (_intr.hasIgnoreMarker(_defaultConstructor)) {
+                    _defaultConstructor = null;
+                }
+            }
+            // count down to allow safe removal
+            for (int i = constructors.size(); --i >= 0; ) {
+                if (_intr.hasIgnoreMarker(constructors.get(i))) {
+                    constructors.remove(i);
+                }
+            }
+            for (int i = factories.size(); --i >= 0; ) {
+                if (_intr.hasIgnoreMarker(factories.get(i))) {
+                    factories.remove(i);
+                }
+            }
+        }
+        return new AnnotatedClass.Creators(_defaultConstructor, constructors, factories);
+    }
+
+    /**
+     * Helper method for locating constructors (and matching mix-in overrides)
+     * we might want to use; this is needed in order to mix information between
+     * the two and construct resulting {@link AnnotatedConstructor}s
+     */
+    private List<AnnotatedConstructor> _findPotentialConstructors(JavaType type,
+            Class<?> primaryMixIn)
+    {
+        ClassUtil.Ctor defaultCtor = null;
+        List<ClassUtil.Ctor> ctors = null;
+
+        // 18-Jun-2016, tatu: Enum constructors will never be useful (unlike
+        //    possibly static factory methods); but they can be royal PITA
+        //    due to some oddities by JVM; see:
+        //    [https://github.com/FasterXML/jackson-module-parameter-names/issues/35]
+        //    for more. So, let's just skip them.
+        if (!type.isEnumType()) {
+            ClassUtil.Ctor[] declaredCtors = ClassUtil.getConstructors(type.getRawClass());
+            for (ClassUtil.Ctor ctor : declaredCtors) {
+                if (!isIncludableConstructor(ctor.getConstructor())) {
+                    continue;
+                }
+                if (ctor.getParamCount() == 0) {
+                    defaultCtor = ctor;
+                } else {
+                    if (ctors == null) {
+                        ctors = new ArrayList<>();
+                    }
+                    ctors.add(ctor);
+                }
+            }
+        }
+        List<AnnotatedConstructor> result;
+        int ctorCount;
+        if (ctors == null) {
+            result = Collections.emptyList();
+            // Nothing found? Short-circuit
+            if (defaultCtor == null) { 
+                return result;
+            }
+            ctorCount = 0;
+        } else {
+            ctorCount = ctors.size();
+            result = new ArrayList<>(ctorCount);
+            for (int i = 0; i < ctorCount; ++i) {
+                result.add(null);
+            }
+        }
+
+        // so far so good; but do we also need to find mix-ins overrides?
+        if (primaryMixIn != null) {
+            MemberKey[] ctorKeys = null;
+            for (ClassUtil.Ctor mixinCtor : ClassUtil.getConstructors(primaryMixIn)) {
+                if (mixinCtor.getParamCount() == 0) {
+                    if (defaultCtor != null) {
+                        _defaultConstructor = constructDefaultConstructor(defaultCtor, mixinCtor);
+                        defaultCtor = null;
+                    }
+                    continue;
+                }
+                if (ctors != null) {
+                    if (ctorKeys == null) {
+                        ctorKeys = new MemberKey[ctorCount];
+                        for (int i = 0; i < ctorCount; ++i) {
+                            ctorKeys[i] = new MemberKey(ctors.get(i).getConstructor());
+                        }
+                    }
+                    MemberKey key = new MemberKey(mixinCtor.getConstructor());
+    
+                    for (int i = 0; i < ctorCount; ++i) {
+                        if (key.equals(ctorKeys[i])) {
+                            result.set(i,
+                                    constructNonDefaultConstructor(ctors.get(i), mixinCtor));
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+        // Ok: anything within mix-ins has been resolved; anything remaining we must resolve
+        if (defaultCtor != null) {
+            _defaultConstructor = constructDefaultConstructor(defaultCtor, null);
+        }
+        for (int i = 0; i < ctorCount; ++i) {
+            AnnotatedConstructor ctor = result.get(i);
+            if (ctor == null) {
+                result.set(i,
+                        constructNonDefaultConstructor(ctors.get(i), null));
+            }
+        }
+        return result;
+    }
+
+    private List<AnnotatedMethod> _findPotentialFactories(JavaType type, Class<?> primaryMixIn)
+    {
+        List<Method> candidates = null;
+
+        // First find all potentially relevant static methods
+        for (Method m : ClassUtil.getClassMethods(type.getRawClass())) {
+            if (!Modifier.isStatic(m.getModifiers())) {
+                continue;
+            }
+            // all factory methods are fine:
+            //int argCount = m.getParameterTypes().length;
+            if (candidates == null) {
+                candidates = new ArrayList<>();
+            }
+            candidates.add(m);
+        }
+        // and then locate mix-ins, if any
+        if (candidates == null) {
+            return Collections.emptyList();
+        }
+        int factoryCount = candidates.size();
+        List<AnnotatedMethod> result = new ArrayList<>(factoryCount);
+        for (int i = 0; i < factoryCount; ++i) {
+            result.add(null);
+        }
+        // so far so good; but do we also need to find mix-ins overrides?
+        if (primaryMixIn != null) {
+            MemberKey[] methodKeys = null;
+            for (Method mixinFactory : ClassUtil.getDeclaredMethods(primaryMixIn)) {
+                if (!Modifier.isStatic(mixinFactory.getModifiers())) {
+                    continue;
+                }
+                if (methodKeys == null) {
+                    methodKeys = new MemberKey[factoryCount];
+                    for (int i = 0; i < factoryCount; ++i) {
+                        methodKeys[i] = new MemberKey(candidates.get(i));
+                    }
+                }
+                MemberKey key = new MemberKey(mixinFactory);
+                for (int i = 0; i < factoryCount; ++i) {
+                    if (key.equals(methodKeys[i])) {
+                        result.set(i,
+                                constructFactoryCreator(candidates.get(i), mixinFactory));
+                        break;
+                    }
+                }
+            }
+        }
+        // Ok: anything within mix-ins has been resolved; anything remaining we must resolve
+        for (int i = 0; i < factoryCount; ++i) {
+            AnnotatedMethod factory = result.get(i);
+            if (factory == null) {
+                result.set(i,
+                        constructFactoryCreator(candidates.get(i), null));
+            }
+        }
+        return result;
+    }
+
+    protected AnnotatedConstructor constructDefaultConstructor(ClassUtil.Ctor ctor,
+            ClassUtil.Ctor mixin)
+    {
+        if (_intr == null) { // when annotation processing is disabled
+            return new AnnotatedConstructor(_typeContext, ctor.getConstructor(),
+                    _emptyAnnotationMap(), NO_ANNOTATION_MAPS);
+        }
+        return new AnnotatedConstructor(_typeContext, ctor.getConstructor(),
+                collectAnnotations(ctor, mixin),
+                collectAnnotations(ctor.getConstructor().getParameterAnnotations(),
+                        (mixin == null) ? null : mixin.getConstructor().getParameterAnnotations()));
+    }
+
+    protected AnnotatedConstructor constructNonDefaultConstructor(ClassUtil.Ctor ctor,
+            ClassUtil.Ctor mixin)
+    {
+        final int paramCount = ctor.getParamCount();
+        if (_intr == null) { // when annotation processing is disabled
+            return new AnnotatedConstructor(_typeContext, ctor.getConstructor(),
+                    _emptyAnnotationMap(), _emptyAnnotationMaps(paramCount));
+        }
+
+        /* Looks like JDK has discrepancy, whereas annotations for implicit 'this'
+         * (for non-static inner classes) are NOT included, but type is?
+         * Strange, sounds like a bug. Alas, we can't really fix that...
+         */
+        if (paramCount == 0) { // no-arg default constructors, can simplify slightly
+            return new AnnotatedConstructor(_typeContext, ctor.getConstructor(),
+                    collectAnnotations(ctor, mixin),
+                    NO_ANNOTATION_MAPS);
+        }
+        // Also: enum value constructors
+        AnnotationMap[] resolvedAnnotations;
+        Annotation[][] paramAnns = ctor.getParameterAnnotations();
+        if (paramCount != paramAnns.length) {
+            // Limits of the work-around (to avoid hiding real errors):
+            // first, only applicable for member classes and then either:
+
+            resolvedAnnotations = null;
+            Class<?> dc = ctor.getDeclaringClass();
+            // (a) is enum, which have two extra hidden params (name, index)
+            if (dc.isEnum() && (paramCount == paramAnns.length + 2)) {
+                Annotation[][] old = paramAnns;
+                paramAnns = new Annotation[old.length+2][];
+                System.arraycopy(old, 0, paramAnns, 2, old.length);
+                resolvedAnnotations = collectAnnotations(paramAnns, null);
+            } else if (dc.isMemberClass()) {
+                // (b) non-static inner classes, get implicit 'this' for parameter, not  annotation
+                if (paramCount == (paramAnns.length + 1)) {
+                    // hack attack: prepend a null entry to make things match
+                    Annotation[][] old = paramAnns;
+                    paramAnns = new Annotation[old.length+1][];
+                    System.arraycopy(old, 0, paramAnns, 1, old.length);
+                    paramAnns[0] = NO_ANNOTATIONS;
+                    resolvedAnnotations = collectAnnotations(paramAnns, null);
+                }
+            }
+            if (resolvedAnnotations == null) {
+                throw new IllegalStateException(String.format(
+"Internal error: constructor for %s has mismatch: %d parameters; %d sets of annotations",
+ctor.getDeclaringClass().getName(), paramCount, paramAnns.length));
+            }
+        } else {
+            resolvedAnnotations = collectAnnotations(paramAnns,
+                    (mixin == null) ? null : mixin.getParameterAnnotations());
+        }
+        return new AnnotatedConstructor(_typeContext, ctor.getConstructor(),
+                collectAnnotations(ctor, mixin), resolvedAnnotations);
+    }
+
+    protected AnnotatedMethod constructFactoryCreator(Method m, Method mixin)
+    {
+        final int paramCount = m.getParameterTypes().length;
+        if (_intr == null) { // when annotation processing is disabled
+            return new AnnotatedMethod(_typeContext, m, _emptyAnnotationMap(),
+                    _emptyAnnotationMaps(paramCount));
+        }
+        if (paramCount == 0) { // common enough we can slightly optimize
+            return new AnnotatedMethod(_typeContext, m, collectAnnotations(m, mixin),
+                    NO_ANNOTATION_MAPS);
+        }
+        return new AnnotatedMethod(_typeContext, m, collectAnnotations(m, mixin),
+                collectAnnotations(m.getParameterAnnotations(),
+                        (mixin == null) ? null : mixin.getParameterAnnotations()));
+    }
+
+    private AnnotationMap[] collectAnnotations(Annotation[][] mainAnns, Annotation[][] mixinAnns) {
+        final int count = mainAnns.length;
+        AnnotationMap[] result = new AnnotationMap[count];
+        for (int i = 0; i < count; ++i) {
+            AnnotationCollector c = collectAnnotations(AnnotationCollector.emptyCollector(),
+                    mainAnns[i]);
+            if (mixinAnns != null) {
+                c = collectAnnotations(c, mixinAnns[i]);
+            }
+            result[i] = c.asAnnotationMap();
+        }
+        return result;
+    }
+
+    // // NOTE: these are only called when we know we have AnnotationIntrospector
+    
+    private AnnotationMap collectAnnotations(ClassUtil.Ctor main, ClassUtil.Ctor mixin) {
+        AnnotationCollector c = collectAnnotations(main.getConstructor().getDeclaredAnnotations());
+        if (mixin != null) {
+            c = collectAnnotations(c, mixin.getConstructor().getDeclaredAnnotations());
+        }
+        return c.asAnnotationMap();
+    }
+
+    private final AnnotationMap collectAnnotations(AnnotatedElement main, AnnotatedElement mixin) {
+        AnnotationCollector c = collectAnnotations(main.getDeclaredAnnotations());
+        if (mixin != null) {
+            c = collectAnnotations(c, mixin.getDeclaredAnnotations());
+        }
+        return c.asAnnotationMap();
+    }
+
+    // for [databind#1005]: do not use or expose synthetic constructors
+    private static boolean isIncludableConstructor(Constructor<?> c) {
+        return !c.isSynthetic();
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedField.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedField.java
index a499407..b81c51e 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedField.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedField.java
@@ -18,7 +18,7 @@
     /**
      * Actual {@link Field} used for access.
      *<p>
-     * Transient since it can not be persisted directly using
+     * Transient since it cannot be persisted directly using
      * JDK serialization
      */
     protected final transient Field _field;
@@ -126,10 +126,6 @@
     /**********************************************************
      */
 
-    public String getFullName() {
-        return getDeclaringClass().getName() + "#" + getName();
-    }
-
     public int getAnnotationCount() { return _annotations.size(); }
 
     /**
@@ -145,8 +141,8 @@
     @Override
     public boolean equals(Object o) {
         if (o == this) return true;
-        if (o == null || o.getClass() != getClass()) return false;
-        return ((AnnotatedField) o)._field == _field;
+        return ClassUtil.hasClass(o, getClass())
+                && (((AnnotatedField) o)._field == _field);
     }
 
     @Override
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedFieldCollector.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedFieldCollector.java
new file mode 100644
index 0000000..0dba88b
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedFieldCollector.java
@@ -0,0 +1,149 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.*;
+
+import com.fasterxml.jackson.databind.AnnotationIntrospector;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.introspect.ClassIntrospector.MixInResolver;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+import com.fasterxml.jackson.databind.util.ClassUtil;
+
+public class AnnotatedFieldCollector
+    extends CollectorBase
+{
+    // // // Configuration
+
+    private final TypeFactory _typeFactory;
+    private final MixInResolver _mixInResolver;
+
+    // // // Collected state
+
+    AnnotatedFieldCollector(AnnotationIntrospector intr,
+            TypeFactory types, MixInResolver mixins)
+    {
+        super(intr);
+        _typeFactory = types;
+        _mixInResolver = (intr == null) ? null : mixins;
+    }
+
+    public static List<AnnotatedField> collectFields(AnnotationIntrospector intr,
+            TypeResolutionContext tc,
+            MixInResolver mixins, TypeFactory types,
+            JavaType type)
+    {
+        return new AnnotatedFieldCollector(intr, types, mixins).collect(tc, type);
+    }
+
+    List<AnnotatedField> collect(TypeResolutionContext tc, JavaType type)
+    {
+        Map<String,FieldBuilder> foundFields = _findFields(tc, type, null);
+        if (foundFields == null) {
+            return Collections.emptyList();
+        }
+        List<AnnotatedField> result = new ArrayList<>(foundFields.size());
+        for (FieldBuilder b : foundFields.values()) {
+            result.add(b.build());
+        }
+        return result;
+    }
+
+    private Map<String,FieldBuilder> _findFields(TypeResolutionContext tc,
+            JavaType type, Map<String,FieldBuilder> fields)
+    {
+        // First, a quick test: we only care for regular classes (not interfaces,
+        //primitive types etc), except for Object.class. A simple check to rule out
+        // other cases is to see if there is a super class or not.
+        JavaType parent = type.getSuperClass();
+        if (parent == null) {
+            return fields;
+        }
+        final Class<?> cls = type.getRawClass();
+        // Let's add super-class' fields first, then ours.
+        fields = _findFields(new TypeResolutionContext.Basic(_typeFactory, parent.getBindings()),
+                parent, fields);
+        for (Field f : ClassUtil.getDeclaredFields(cls)) {
+            // static fields not included (transients are at this point, filtered out later)
+            if (!_isIncludableField(f)) {
+                continue;
+            }
+            // Ok now: we can (and need) not filter out ignorable fields at this point; partly
+            // because mix-ins haven't been added, and partly because logic can be done
+            // when determining get/settability of the field.
+            if (fields == null) {
+                fields = new LinkedHashMap<>();
+            }
+            FieldBuilder b = new FieldBuilder(tc, f);
+            if (_intr != null) {
+                b.annotations = collectAnnotations(b.annotations, f.getDeclaredAnnotations());
+            }
+            fields.put(f.getName(), b);
+        }
+        // And then... any mix-in overrides?
+        if (_mixInResolver != null) {
+            Class<?> mixin = _mixInResolver.findMixInClassFor(cls);
+            if (mixin != null) {
+                _addFieldMixIns(mixin, cls, fields);
+            }
+        }
+        return fields;
+    }
+
+    /**
+     * Method called to add field mix-ins from given mix-in class (and its fields)
+     * into already collected actual fields (from introspected classes and their
+     * super-classes)
+     */
+    private void _addFieldMixIns(Class<?> mixInCls, Class<?> targetClass,
+            Map<String,FieldBuilder> fields)
+    {
+        List<Class<?>> parents = ClassUtil.findSuperClasses(mixInCls, targetClass, true);
+        for (Class<?> mixin : parents) {
+            for (Field mixinField : ClassUtil.getDeclaredFields(mixin)) {
+                // there are some dummy things (static, synthetic); better ignore
+                if (!_isIncludableField(mixinField)) {
+                    continue;
+                }
+                String name = mixinField.getName();
+                // anything to mask? (if not, quietly ignore)
+                FieldBuilder b = fields.get(name);
+                if (b != null) {
+                    b.annotations = collectAnnotations(b.annotations, mixinField.getDeclaredAnnotations());
+                }
+            }
+        }
+    }
+
+    private boolean _isIncludableField(Field f)
+    {
+        // Most likely synthetic fields, if any, are to be skipped similar to methods
+        if (f.isSynthetic()) {
+            return false;
+        }
+        // Static fields are never included. Transient are (since 2.6), for
+        // purpose of propagating removal
+        int mods = f.getModifiers();
+        if (Modifier.isStatic(mods)) {
+            return false;
+        }
+        return true;
+    }
+
+    private final static class FieldBuilder {
+        public final TypeResolutionContext typeContext;
+        public final Field field;
+
+        public AnnotationCollector annotations;
+
+        public FieldBuilder(TypeResolutionContext tc, Field f) {
+            typeContext = tc;
+            field = f;
+            annotations = AnnotationCollector.emptyCollector();
+        }
+
+        public AnnotatedField build() {
+            return new AnnotatedField(typeContext, field, annotations.asAnnotationMap());
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMember.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMember.java
index 423f209..0f7f3d2 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMember.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMember.java
@@ -47,7 +47,15 @@
         _typeContext = base._typeContext;
         _annotations = base._annotations;
     }
-    
+
+    /**
+     * Fluent factory method that will construct a new instance that uses specified
+     * instance annotations instead of currently configured ones.
+     *
+     * @since 2.9 (promoted from `Annotated`)
+     */
+    public abstract Annotated withAnnotations(AnnotationMap fallback);
+
     /**
      * Actual physical class in which this memmber was declared.
      */
@@ -55,12 +63,19 @@
 
     public abstract Member getMember();
 
+    public String getFullName() {
+        return getDeclaringClass().getName() + "#" + getName();
+    }
+
     /**
      * Accessor for {@link TypeResolutionContext} that is used for resolving
      * full generic type of this member.
      * 
      * @since 2.7
+     *
+     * @deprecated Since 2.9
      */
+    @Deprecated
     public TypeResolutionContext getTypeContext() {
         return _typeContext;
     }
@@ -88,39 +103,25 @@
         }
         return _annotations.hasOneOf(annoClasses);
     }
-    
+
     @Override
+    @Deprecated
     public Iterable<Annotation> annotations() {
         if (_annotations == null) {
             return Collections.emptyList();
         }
         return _annotations.annotations();
     }
-    
-    @Override
-    protected AnnotationMap getAllAnnotations() {
+
+    /**
+     *<p>
+     * NOTE: promoted in 2.9 from `Annotated` up
+     */
+    public AnnotationMap getAllAnnotations() { // alas, used by at least one module, hence public
         return _annotations;
     }
 
     /**
-     * Method called to override an annotation, usually due to a mix-in
-     * annotation masking or overriding an annotation 'real' constructor
-     * has.
-     */
-    public final boolean addOrOverride(Annotation a) {
-        return _annotations.add(a);
-    }
-
-    /**
-     * Method called to augment annotations, by adding specified
-     * annotation if and only if it is not yet present in the
-     * annotation map we have.
-     */
-    public final boolean addIfNotPresent(Annotation a) {
-        return _annotations.addIfNotPresent(a);
-    }
-
-    /**
      * Method that can be called to modify access rights, by calling
      * {@link java.lang.reflect.AccessibleObject#setAccessible} on
      * the underlying annotated element.
@@ -140,15 +141,6 @@
     }
 
     /**
-     * @deprecated Since 2.7 use {@link #fixAccess(boolean)} instead
-     */
-    @Deprecated
-    public final void fixAccess() {
-//        fixAccess(false);
-        fixAccess(true);
-    }
-
-    /**
      * Optional method that can be used to assign value of
      * this member on given object, if this is a supported
      * operation for member type.
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMethod.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMethod.java
index e7d75ac..2338987 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMethod.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMethod.java
@@ -36,7 +36,7 @@
     {
         super(ctxt, classAnn, paramAnnotations);
         if (method == null) {
-            throw new IllegalArgumentException("Can not construct AnnotatedMethod with null Method");
+            throw new IllegalArgumentException("Cannot construct AnnotatedMethod with null Method");
         }
         _method = method;
     }
@@ -51,15 +51,7 @@
         _method = null;
         _serialization = ser;
     }
-    
-    /**
-     * Method that constructs a new instance with settings (annotations, parameter annotations)
-     * of this instance, but with different physical {@link Method}.
-     */
-    public AnnotatedMethod withMethod(Method m) {
-        return new AnnotatedMethod(_typeContext, m, _annotations, _paramAnnotations);
-    }
-    
+
     @Override
     public AnnotatedMethod withAnnotations(AnnotationMap ann) {
         return new AnnotatedMethod(_typeContext, _method, ann, _paramAnnotations);
@@ -123,7 +115,7 @@
     }
 
     public final Object callOn(Object pojo) throws Exception {
-        return _method.invoke(pojo);
+        return _method.invoke(pojo, (Object[]) null);
     }
 
     public final Object callOnWith(Object pojo, Object... args) throws Exception {
@@ -178,10 +170,7 @@
     {
         try {
             _method.invoke(pojo, value);
-        } catch (IllegalAccessException e) {
-            throw new IllegalArgumentException("Failed to setValue() with method "
-                    +getFullName()+": "+e.getMessage(), e);
-        } catch (InvocationTargetException e) {
+        } catch (IllegalAccessException | InvocationTargetException e) {
             throw new IllegalArgumentException("Failed to setValue() with method "
                     +getFullName()+": "+e.getMessage(), e);
         }
@@ -191,11 +180,8 @@
     public Object getValue(Object pojo) throws IllegalArgumentException
     {
         try {
-            return _method.invoke(pojo);
-        } catch (IllegalAccessException e) {
-            throw new IllegalArgumentException("Failed to getValue() with method "
-                    +getFullName()+": "+e.getMessage(), e);
-        } catch (InvocationTargetException e) {
+            return _method.invoke(pojo, (Object[]) null);
+        } catch (IllegalAccessException | InvocationTargetException e) {
             throw new IllegalArgumentException("Failed to getValue() with method "
                     +getFullName()+": "+e.getMessage(), e);
         }
@@ -207,9 +193,9 @@
     /*****************************************************
      */
 
+    @Override
     public String getFullName() {
-        return getDeclaringClass().getName() + "#" + getName() + "("
-            +getParameterCount()+" params)";
+        return String.format("%s(%d params)", super.getFullName(), getParameterCount());
     }
 
     public Class<?>[] getRawParameterTypes()
@@ -260,10 +246,10 @@
     @Override
     public boolean equals(Object o) {
         if (o == this) return true;
-        if (o == null || o.getClass() != getClass()) return false;
-        return ((AnnotatedMethod) o)._method == _method;
+        return ClassUtil.hasClass(o, getClass())
+                && (((AnnotatedMethod) o)._method == _method);
     }
-    
+
     /*
     /**********************************************************
     /* JDK serialization handling
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMethodCollector.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMethodCollector.java
new file mode 100644
index 0000000..0341e3a
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMethodCollector.java
@@ -0,0 +1,207 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.*;
+
+import com.fasterxml.jackson.databind.AnnotationIntrospector;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.introspect.ClassIntrospector.MixInResolver;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+import com.fasterxml.jackson.databind.util.ClassUtil;
+
+public class AnnotatedMethodCollector
+    extends CollectorBase
+{
+    private final MixInResolver _mixInResolver;
+
+    AnnotatedMethodCollector(AnnotationIntrospector intr,
+            MixInResolver mixins)
+    {
+        super(intr);
+        _mixInResolver = (intr == null) ? null : mixins;
+    }
+
+    public static AnnotatedMethodMap collectMethods(AnnotationIntrospector intr,
+            TypeResolutionContext tc,
+            MixInResolver mixins, TypeFactory types,
+            JavaType type, List<JavaType> superTypes, Class<?> primaryMixIn)
+    {
+        // Constructor also always members of resolved class, parent == resolution context
+        return new AnnotatedMethodCollector(intr, mixins)
+                .collect(types, tc, type, superTypes, primaryMixIn);
+    }
+
+    AnnotatedMethodMap collect(TypeFactory typeFactory, TypeResolutionContext tc,
+            JavaType mainType, List<JavaType> superTypes, Class<?> primaryMixIn)
+    {
+        Map<MemberKey,MethodBuilder> methods = new LinkedHashMap<>();
+        
+        // first: methods from the class itself
+        _addMemberMethods(tc, mainType.getRawClass(), methods, primaryMixIn);
+
+        // and then augment these with annotations from super-types:
+        for (JavaType type : superTypes) {
+            Class<?> mixin = (_mixInResolver == null) ? null : _mixInResolver.findMixInClassFor(type.getRawClass());
+            _addMemberMethods(
+                    new TypeResolutionContext.Basic(typeFactory, type.getBindings()),
+                    type.getRawClass(), methods, mixin);
+        }
+        // Special case: mix-ins for Object.class? (to apply to ALL classes)
+        boolean checkJavaLangObject = false;
+        if (_mixInResolver != null) {
+            Class<?> mixin = _mixInResolver.findMixInClassFor(Object.class);
+            if (mixin != null) {
+                _addMethodMixIns(tc, mainType.getRawClass(), methods, mixin); //, mixins);
+                checkJavaLangObject = true;
+            }
+        }
+
+        // Any unmatched mix-ins? Most likely error cases (not matching any method);
+        // but there is one possible real use case: exposing Object#hashCode
+        // (alas, Object#getClass can NOT be exposed)
+        // Since we only know of that ONE case, optimize for it
+        if (checkJavaLangObject && (_intr != null) && !methods.isEmpty()) {
+            // Could use lookup but probably as fast or faster to traverse
+            for (Map.Entry<MemberKey,MethodBuilder> entry : methods.entrySet()) {
+                MemberKey k = entry.getKey();
+                if (!"hashCode".equals(k.getName()) || (0 != k.argCount())) {
+                    continue;
+                }
+                try {
+                    // And with that, we can generate it appropriately
+                    Method m = Object.class.getDeclaredMethod(k.getName());
+                    if (m != null) {
+                        MethodBuilder b = entry.getValue();
+                        b.annotations = collectDefaultAnnotations(b.annotations,
+                                m.getDeclaredAnnotations());
+                        b.method = m;
+                   }
+                } catch (Exception e) { }
+            }
+        }
+
+        // And then let's create the lookup map
+        if (methods.isEmpty()) {
+            return new AnnotatedMethodMap();
+        }
+        Map<MemberKey,AnnotatedMethod> actual = new LinkedHashMap<>(methods.size());
+        for (Map.Entry<MemberKey,MethodBuilder> entry : methods.entrySet()) {
+            AnnotatedMethod am = entry.getValue().build();
+            if (am != null) {
+                actual.put(entry.getKey(), am);
+            }
+        }
+        return new AnnotatedMethodMap(actual);
+    }
+
+    private void _addMemberMethods(TypeResolutionContext tc,
+            Class<?> cls, Map<MemberKey,MethodBuilder> methods, Class<?> mixInCls)
+    {
+        // first, mixIns, since they have higher priority then class methods
+        if (mixInCls != null) {
+            _addMethodMixIns(tc, cls, methods, mixInCls);
+        }
+        if (cls == null) { // just so caller need not check when passing super-class
+            return;
+        }
+        // then methods from the class itself
+        for (Method m : ClassUtil.getClassMethods(cls)) {
+            if (!_isIncludableMemberMethod(m)) {
+                continue;
+            }
+            final MemberKey key = new MemberKey(m);
+            MethodBuilder b = methods.get(key);
+            if (b == null) {
+                AnnotationCollector c = (_intr == null) ? AnnotationCollector.emptyCollector()
+                        : collectAnnotations(m.getDeclaredAnnotations());
+                methods.put(key, new MethodBuilder(tc, m, c));
+            } else {
+                if (_intr != null) {
+                    b.annotations = collectDefaultAnnotations(b.annotations, m.getDeclaredAnnotations());
+                }
+                Method old = b.method;
+                if (old == null) { // had "mix-over", replace
+                    b.method = m;
+//                } else if (old.getDeclaringClass().isInterface() && !m.getDeclaringClass().isInterface()) {
+                } else if (Modifier.isAbstract(old.getModifiers())
+                        && !Modifier.isAbstract(m.getModifiers())) {
+                    // 06-Jan-2010, tatu: Except that if method we saw first is
+                    // from an interface, and we now find a non-interface definition, we should
+                    //   use this method, but with combination of annotations.
+                    //   This helps (or rather, is essential) with JAXB annotations and
+                    //   may also result in faster method calls (interface calls are slightly
+                    //   costlier than regular method calls)
+                    b.method = m;
+                    // 23-Aug-2017, tatu: [databind#1705] Also need to change the type resolution context if so
+                    //    (note: mix-over case above shouldn't need it)
+                    b.typeContext = tc;
+                }
+            }
+        }
+    }
+
+    protected void _addMethodMixIns(TypeResolutionContext tc, Class<?> targetClass,
+            Map<MemberKey,MethodBuilder> methods, Class<?> mixInCls)
+    {
+        if (_intr == null) {
+            return;
+        }
+        for (Class<?> mixin : ClassUtil.findRawSuperTypes(mixInCls, targetClass, true)) {
+            for (Method m : ClassUtil.getDeclaredMethods(mixin)) {
+                if (!_isIncludableMemberMethod(m)) {
+                    continue;
+                }
+                final MemberKey key = new MemberKey(m);
+                MethodBuilder b = methods.get(key);
+                Annotation[] anns = m.getDeclaredAnnotations();
+                if (b == null) {
+                    // nothing yet; add but do NOT specify method -- this marks it
+                    // as "mix-over", floating mix-in
+                    methods.put(key, new MethodBuilder(tc, null, collectAnnotations(anns)));
+                } else {
+                    b.annotations = collectDefaultAnnotations(b.annotations, anns);
+                }
+            }
+        }
+    }
+
+    private boolean _isIncludableMemberMethod(Method m)
+    {
+        if (Modifier.isStatic(m.getModifiers())
+                // Looks like generics can introduce hidden bridge and/or synthetic methods.
+                // I don't think we want to consider those...
+                || m.isSynthetic() || m.isBridge()) {
+            return false;
+        }
+        // also, for now we have no use for methods with more than 2 arguments:
+        // (2 argument methods for "any setter", fwtw)
+        int pcount = m.getParameterTypes().length;
+        return (pcount <= 2);
+    }
+
+    private final static class MethodBuilder {
+        public TypeResolutionContext typeContext;
+
+        // Method left empty for "floating" mix-in, filled in as need be
+        public Method method;
+        public AnnotationCollector annotations;
+
+        public MethodBuilder(TypeResolutionContext tc, Method m,
+                AnnotationCollector ann) {
+            typeContext = tc;
+            method = m;
+            annotations = ann;
+        }
+
+        public AnnotatedMethod build() {
+            if (method == null) {
+                return null;
+            }
+            // 12-Apr-2017, tatu: Note that parameter annotations are NOT collected -- we could
+            //   collect them if that'd make sense but...
+            return new AnnotatedMethod(typeContext, method, annotations.asAnnotationMap(), null);
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMethodMap.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMethodMap.java
index 0cfb387..dcd08a2 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMethodMap.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMethodMap.java
@@ -11,40 +11,15 @@
 public final class AnnotatedMethodMap
     implements Iterable<AnnotatedMethod>
 {
-    protected LinkedHashMap<MemberKey,AnnotatedMethod> _methods;
+    protected Map<MemberKey,AnnotatedMethod> _methods;
 
     public AnnotatedMethodMap() { }
 
     /**
-     * Method called to add specified annotated method in the Map.
+     * @since 2.9
      */
-    public void add(AnnotatedMethod am)
-    {
-        if (_methods == null) {
-            _methods = new LinkedHashMap<MemberKey,AnnotatedMethod>();
-        }
-        _methods.put(new MemberKey(am.getAnnotated()), am);
-    }
-
-    /**
-     * Method called to remove specified method, assuming
-     * it exists in the Map
-     */
-    public AnnotatedMethod remove(AnnotatedMethod am)
-    {
-        return remove(am.getAnnotated());
-    }
-
-    public AnnotatedMethod remove(Method m)
-    {
-        if (_methods != null) {
-            return _methods.remove(new MemberKey(m));
-        }
-        return null;
-    }
-
-    public boolean isEmpty() {
-        return (_methods == null || _methods.size() == 0);
+    public AnnotatedMethodMap(Map<MemberKey,AnnotatedMethod> m) {
+        _methods = m;
     }
 
     public int size() {
@@ -76,10 +51,9 @@
     @Override
     public Iterator<AnnotatedMethod> iterator()
     {
-        if (_methods != null) {
-            return _methods.values().iterator();
+        if (_methods == null) {
+            return Collections.emptyIterator();
         }
-        List<AnnotatedMethod> empty = Collections.emptyList();
-        return empty.iterator();
+        return _methods.values().iterator();
     }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedParameter.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedParameter.java
index 62ce143..9835c35 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedParameter.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedParameter.java
@@ -3,11 +3,12 @@
 import java.lang.reflect.*;
 
 import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.util.ClassUtil;
 
 /**
  * Object that represents method parameters, mostly so that associated
  * annotations can be processed conveniently. Note that many of accessors
- * can not return meaningful values since parameters do not have stand-alone
+ * cannot return meaningful values since parameters do not have stand-alone
  * JDK objects associated; so access should mostly be limited to checking
  * annotation values which are properly aggregated and included.
  */
@@ -30,7 +31,7 @@
      * Index of the parameter within argument list
      */
     protected final int _index;
-    
+
     /*
     /**********************************************************
     /* Life-cycle
@@ -38,9 +39,10 @@
      */
 
     public AnnotatedParameter(AnnotatedWithParams owner, JavaType type,
+            TypeResolutionContext typeContext,
             AnnotationMap annotations, int index)
     {
-        super((owner == null) ? null : owner.getTypeContext(), annotations);
+        super(typeContext, annotations);
         _owner = owner;
         _type = type;
         _index = index;
@@ -167,7 +169,9 @@
     @Override
     public boolean equals(Object o) {
         if (o == this) return true;
-        if (o == null || o.getClass() != getClass()) return false;
+        if (!ClassUtil.hasClass(o, getClass())) {
+            return false;
+        }
         AnnotatedParameter other = (AnnotatedParameter) o;
         return other._owner.equals(_owner) && (other._index == _index);
     }
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedWithParams.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedWithParams.java
index 8cf5a79..4f415a6 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedWithParams.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedWithParams.java
@@ -84,7 +84,7 @@
 
     public final AnnotatedParameter getParameter(int index) {
         return new AnnotatedParameter(this, getParameterType(index),
-                getParameterAnnotations(index), index);
+                _typeContext, getParameterAnnotations(index), index);
     }
 
     public abstract int getParameterCount();
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationCollector.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationCollector.java
new file mode 100644
index 0000000..940d435
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationCollector.java
@@ -0,0 +1,302 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import java.lang.annotation.Annotation;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import com.fasterxml.jackson.databind.util.Annotations;
+
+/**
+ * Helper class used to collect annotations to be stored as
+ * {@link com.fasterxml.jackson.databind.util.Annotations} (like {@link AnnotationMap}).
+ *
+ * @since 2.9
+ */
+public abstract class AnnotationCollector
+{
+    protected final static Annotations NO_ANNOTATIONS = new NoAnnotations();
+
+    /**
+     * Optional data to carry along
+     */
+    protected final Object _data;
+
+    protected AnnotationCollector(Object d) {
+        _data = d;
+    }
+
+    public static Annotations emptyAnnotations() { return NO_ANNOTATIONS; }
+
+    public static AnnotationCollector emptyCollector() {
+        return EmptyCollector.instance;
+    }
+
+    public static AnnotationCollector emptyCollector(Object data) {
+        return new EmptyCollector(data);
+    }
+
+    public abstract Annotations asAnnotations();
+    public abstract AnnotationMap asAnnotationMap();
+
+    public Object getData() {
+        return _data;
+    }
+
+    /*
+    /**********************************************************
+    /* API
+    /**********************************************************
+     */
+
+    public abstract boolean isPresent(Annotation ann);
+
+    public abstract AnnotationCollector addOrOverride(Annotation ann);
+
+    /*
+    /**********************************************************
+    /* Collector implementations
+    /**********************************************************
+     */
+
+    static class EmptyCollector extends AnnotationCollector
+    {
+        public final static EmptyCollector instance = new EmptyCollector(null);
+
+        EmptyCollector(Object data) { super(data); }
+
+        @Override
+        public Annotations asAnnotations() {
+            return NO_ANNOTATIONS;
+        }
+ 
+        @Override
+        public AnnotationMap asAnnotationMap() {
+            return new AnnotationMap();
+        }
+
+        @Override
+        public boolean isPresent(Annotation ann) { return false; }
+
+        @Override
+        public AnnotationCollector addOrOverride(Annotation ann) {
+            return new OneCollector(_data, ann.annotationType(), ann);
+        }
+    }
+
+    static class OneCollector extends AnnotationCollector
+    {
+        private Class<?> _type;
+        private Annotation _value;
+
+        public OneCollector(Object data,
+                Class<?> type, Annotation value) {
+            super(data);
+            _type = type;
+            _value = value;
+        }
+
+        @Override
+        public Annotations asAnnotations() {
+            return new OneAnnotation(_type, _value);
+        }
+
+        @Override
+        public AnnotationMap asAnnotationMap() {
+            return AnnotationMap.of(_type, _value);
+        }
+
+        @Override
+        public boolean isPresent(Annotation ann) {
+            return ann.annotationType() == _type;
+        }
+        
+        @Override
+        public AnnotationCollector addOrOverride(Annotation ann) {
+            final Class<?> type = ann.annotationType();
+            // true override? Just replace in-place, return
+            if (_type == type) {
+                _value = ann;
+                return this;
+            }
+            return new NCollector(_data, _type, _value, type, ann);
+        }
+    }
+
+    static class NCollector extends AnnotationCollector
+    {
+        protected final HashMap<Class<?>,Annotation> _annotations;
+
+        public NCollector(Object data,
+                Class<?> type1, Annotation value1,
+                Class<?> type2, Annotation value2) {
+            super(data);
+            _annotations = new HashMap<>();
+            _annotations.put(type1, value1);
+            _annotations.put(type2, value2);
+        }
+
+        @Override
+        public Annotations asAnnotations() {
+            if (_annotations.size() == 2) {
+                Iterator<Map.Entry<Class<?>,Annotation>> it = _annotations.entrySet().iterator();
+                Map.Entry<Class<?>,Annotation> en1 = it.next(), en2 = it.next();
+                return new TwoAnnotations(en1.getKey(), en1.getValue(),
+                        en2.getKey(), en2.getValue());
+            }
+            return new AnnotationMap(_annotations);
+        }
+
+        @Override
+        public AnnotationMap asAnnotationMap() {
+            AnnotationMap result = new AnnotationMap();
+            for (Annotation ann : _annotations.values()) {
+                result.add(ann);
+            }
+            return result;
+        }
+
+        @Override
+        public boolean isPresent(Annotation ann) {
+            return _annotations.containsKey(ann.annotationType());
+        }
+
+        @Override
+        public AnnotationCollector addOrOverride(Annotation ann) {
+            _annotations.put(ann.annotationType(), ann);
+            return this;
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Annotations implementations
+    /**********************************************************
+     */
+
+    /**
+     * Immutable implementation for case where no annotations are associated with
+     * an annotatable entity.
+     *
+     * @since 2.9
+     */
+    public static class NoAnnotations
+        implements Annotations, java.io.Serializable
+    {
+        private static final long serialVersionUID = 1L;
+
+        NoAnnotations() { }
+
+        @Override
+        public <A extends Annotation> A get(Class<A> cls) {
+            return null;
+        }
+
+        @Override
+        public boolean has(Class<?> cls) {
+            return false;
+        }
+
+        @Override
+        public boolean hasOneOf(Class<? extends Annotation>[] annoClasses) {
+            return false;
+        }
+
+        @Override
+        public int size() {
+            return 0;
+        }
+    }
+
+    public static class OneAnnotation
+        implements Annotations, java.io.Serializable
+    {
+        private static final long serialVersionUID = 1L;
+
+        private final Class<?> _type;
+        private final Annotation _value;
+
+        public OneAnnotation(Class<?> type, Annotation value) {
+            _type = type;
+            _value = value;
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public <A extends Annotation> A get(Class<A> cls) {
+            if (_type == cls) {
+                return (A) _value;
+            }
+            return null;
+        }
+
+        @Override
+        public boolean has(Class<?> cls) {
+            return (_type == cls);
+        }
+
+        @Override
+        public boolean hasOneOf(Class<? extends Annotation>[] annoClasses) {
+            for (Class<?> cls : annoClasses) {
+                if (cls == _type) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        @Override
+        public int size() {
+            return 1;
+        }
+    }
+
+    public static class TwoAnnotations
+        implements Annotations, java.io.Serializable
+    {
+        private static final long serialVersionUID = 1L;
+    
+        private final Class<?> _type1, _type2;
+        private final Annotation _value1, _value2;
+    
+        public TwoAnnotations(Class<?> type1, Annotation value1,
+                Class<?> type2, Annotation value2) {
+            _type1 = type1;
+            _value1 = value1;
+            _type2 = type2;
+            _value2 = value2;
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public <A extends Annotation> A get(Class<A> cls) {
+            if (_type1 == cls) {
+                return (A) _value1;
+            }
+            if (_type2 == cls) {
+                return (A) _value2;
+            }
+            return null;
+        }
+
+        @Override
+        public boolean has(Class<?> cls) {
+            return (_type1 == cls) || (_type2 == cls);
+        }
+
+        @Override
+        public boolean hasOneOf(Class<? extends Annotation>[] annoClasses) {
+            for (Class<?> cls : annoClasses) {
+                if ((cls == _type1) || (cls == _type2)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        @Override
+        public int size() {
+            return 2;
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair.java
index 97e5edc..aa288b9 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair.java
@@ -5,11 +5,13 @@
 import java.util.Collection;
 import java.util.List;
 
+import com.fasterxml.jackson.annotation.JacksonInject;
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonSetter;
 import com.fasterxml.jackson.core.Version;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
@@ -119,43 +121,9 @@
     {
         JsonIgnoreProperties.Value v2 = _secondary.findPropertyIgnorals(a);
         JsonIgnoreProperties.Value v1 = _primary.findPropertyIgnorals(a);
-
-        if (v2 == null) { // shouldn't occur but
-            return v1;
-        }
-        return v2.withOverrides(v1);
+        return (v2 == null) // shouldn't occur but
+            ? v1 : v2.withOverrides(v1);
     }
-    
-    @Override
-    @Deprecated // since 2.6
-    public String[] findPropertiesToIgnore(Annotated ac) {
-        String[] result = _primary.findPropertiesToIgnore(ac);
-        if (result == null) {
-            result = _secondary.findPropertiesToIgnore(ac);
-        }
-        return result;            
-    }
-
-    @Override
-    @Deprecated // since 2.8
-    public String[] findPropertiesToIgnore(Annotated ac, boolean forSerialization) {
-        String[] result = _primary.findPropertiesToIgnore(ac, forSerialization);
-        if (result == null) {
-            result = _secondary.findPropertiesToIgnore(ac, forSerialization);
-        }
-        return result;            
-    }
-
-    @Override
-    @Deprecated // since 2.8
-    public Boolean findIgnoreUnknownProperties(AnnotatedClass ac)
-    {
-        Boolean result = _primary.findIgnoreUnknownProperties(ac);
-        if (result == null) {
-            result = _secondary.findIgnoreUnknownProperties(ac);
-        }
-        return result;
-    }        
 
     @Override
     public Boolean isIgnorableType(AnnotatedClass ac)
@@ -196,6 +164,37 @@
         return str;
     }
 
+    @Override
+    @Deprecated // since 2.6
+    public String[] findPropertiesToIgnore(Annotated ac) {
+        String[] result = _primary.findPropertiesToIgnore(ac);
+        if (result == null) {
+            result = _secondary.findPropertiesToIgnore(ac);
+        }
+        return result;            
+    }
+
+    @Override
+    @Deprecated // since 2.8
+    public String[] findPropertiesToIgnore(Annotated ac, boolean forSerialization) {
+        String[] result = _primary.findPropertiesToIgnore(ac, forSerialization);
+        if (result == null) {
+            result = _secondary.findPropertiesToIgnore(ac, forSerialization);
+        }
+        return result;            
+    }
+
+    @Override
+    @Deprecated // since 2.8
+    public Boolean findIgnoreUnknownProperties(AnnotatedClass ac)
+    {
+        Boolean result = _primary.findIgnoreUnknownProperties(ac);
+        if (result == null) {
+            result = _secondary.findIgnoreUnknownProperties(ac);
+        }
+        return result;
+    }        
+
     /*
     /******************************************************
     /* Property auto-detection
@@ -217,8 +216,8 @@
     /******************************************************
     /* Type handling
     /******************************************************
-    */
-    
+     */
+
     @Override
     public TypeResolverBuilder<?> findTypeResolver(MapperConfig<?> config,
             AnnotatedClass ac, JavaType baseType)
@@ -274,8 +273,11 @@
         }
         return name;
     }
-    
-    // // // General member (field, method/constructor) annotations
+    /*
+    /******************************************************
+    /* General member (field, method/constructor) annotations
+    /******************************************************
+     */
     
     @Override        
     public ReferenceProperty findReferenceType(AnnotatedMember member) {
@@ -290,50 +292,69 @@
     }
 
     @Override
-    public Object findInjectableValueId(AnnotatedMember m) {
-        Object r = _primary.findInjectableValueId(m);
-        return (r == null) ? _secondary.findInjectableValueId(m) : r;
+    public JacksonInject.Value findInjectableValue(AnnotatedMember m) {
+        JacksonInject.Value r = _primary.findInjectableValue(m);
+        return (r == null) ? _secondary.findInjectableValue(m) : r;
     }
 
     @Override
     public boolean hasIgnoreMarker(AnnotatedMember m) {
         return _primary.hasIgnoreMarker(m) || _secondary.hasIgnoreMarker(m);
     }
-    
+
     @Override
     public Boolean hasRequiredMarker(AnnotatedMember m) {
         Boolean r = _primary.hasRequiredMarker(m);
         return (r == null) ? _secondary.hasRequiredMarker(m) : r;
     }
-    
+
+    @Override
+    @Deprecated // since 2.9
+    public Object findInjectableValueId(AnnotatedMember m) {
+        Object r = _primary.findInjectableValueId(m);
+        return (r == null) ? _secondary.findInjectableValueId(m) : r;
+    }
+
     // // // Serialization: general annotations
 
     @Override
     public Object findSerializer(Annotated am) {
         Object r = _primary.findSerializer(am);
-        return _isExplicitClassOrOb(r, JsonSerializer.None.class)
-                ? r : _secondary.findSerializer(am);
+        if (_isExplicitClassOrOb(r, JsonSerializer.None.class)) {
+            return r;
+        }
+        return _explicitClassOrOb(_secondary.findSerializer(am),
+                JsonSerializer.None.class);
     }
     
     @Override
     public Object findKeySerializer(Annotated a) {
         Object r = _primary.findKeySerializer(a);
-        return _isExplicitClassOrOb(r, JsonSerializer.None.class)
-        		? r : _secondary.findKeySerializer(a);
+        if (_isExplicitClassOrOb(r, JsonSerializer.None.class)) {
+            return r;
+        }
+        return _explicitClassOrOb(_secondary.findKeySerializer(a),
+                JsonSerializer.None.class);
     }
 
     @Override
     public Object findContentSerializer(Annotated a) {
         Object r = _primary.findContentSerializer(a);
-        return _isExplicitClassOrOb(r, JsonSerializer.None.class)
-        		? r : _secondary.findContentSerializer(a);
+        if (_isExplicitClassOrOb(r, JsonSerializer.None.class)) {
+            return r;
+        }
+        return _explicitClassOrOb(_secondary.findContentSerializer(a),
+                JsonSerializer.None.class);
     }
     
     @Override
     public Object findNullSerializer(Annotated a) {
         Object r = _primary.findNullSerializer(a);
-        return _isExplicitClassOrOb(r, JsonSerializer.None.class)
-                ? r : _secondary.findNullSerializer(a);
+        if (_isExplicitClassOrOb(r, JsonSerializer.None.class)) {
+            return r;
+        }
+        return _explicitClassOrOb(_secondary.findNullSerializer(a),
+                JsonSerializer.None.class);
     }
     
     @Deprecated
@@ -464,9 +485,15 @@
     }
 
     @Override
-    public String findImplicitPropertyName(AnnotatedMember param) {
-        String r = _primary.findImplicitPropertyName(param);
-        return (r == null) ? _secondary.findImplicitPropertyName(param) : r;
+    public String findImplicitPropertyName(AnnotatedMember ann) {
+        String r = _primary.findImplicitPropertyName(ann);
+        return (r == null) ? _secondary.findImplicitPropertyName(ann) : r;
+    }
+
+    @Override
+    public List<PropertyName> findPropertyAliases(Annotated ann) {
+        List<PropertyName> r = _primary.findPropertyAliases(ann);
+        return (r == null) ? _secondary.findPropertyAliases(ann) : r;
     }
 
     @Override
@@ -562,18 +589,24 @@
         }
         return n;
     }
-    
+
     @Override
-    public boolean hasAsValueAnnotation(AnnotatedMethod am) {
-        return _primary.hasAsValueAnnotation(am) || _secondary.hasAsValueAnnotation(am);
+    public Boolean hasAsValue(Annotated a) {
+        Boolean b = _primary.hasAsValue(a);
+        if (b == null) {
+            b = _secondary.hasAsValue(a);
+        }
+        return b;
     }
 
     @Override
-    @Deprecated
-    public String findEnumValue(Enum<?> value) {
-        String r = _primary.findEnumValue(value);
-        return (r == null) ? _secondary.findEnumValue(value) : r;
-    }        
+    public Boolean hasAnyGetter(Annotated a) {
+        Boolean b = _primary.hasAnyGetter(a);
+        if (b == null) {
+            b = _secondary.hasAnyGetter(a);
+        }
+        return b;
+    }
 
     @Override
     public  String[] findEnumValues(Class<?> enumType, Enum<?>[] enumValues, String[] names) {
@@ -589,27 +622,56 @@
         return (en == null) ? _secondary.findDefaultEnumValue(enumCls) : en;
     }
 
-    // // // Deserialization: general annotations
+    @Override
+    @Deprecated // since 2.8
+    public String findEnumValue(Enum<?> value) {
+        String r = _primary.findEnumValue(value);
+        return (r == null) ? _secondary.findEnumValue(value) : r;
+    }        
 
     @Override
-    public Object findDeserializer(Annotated am) {
-        Object r = _primary.findDeserializer(am);
-        return _isExplicitClassOrOb(r, JsonDeserializer.None.class)
-                ? r : _secondary.findDeserializer(am);
+    @Deprecated // since 2.9
+    public boolean hasAsValueAnnotation(AnnotatedMethod am) {
+        return _primary.hasAsValueAnnotation(am) || _secondary.hasAsValueAnnotation(am);
     }
     
     @Override
-    public Object findKeyDeserializer(Annotated am) {
-        Object r = _primary.findKeyDeserializer(am);
-        return _isExplicitClassOrOb(r, KeyDeserializer.None.class)
-                ? r : _secondary.findKeyDeserializer(am);
+    @Deprecated // since 2.9
+    public boolean hasAnyGetterAnnotation(AnnotatedMethod am) {
+        return _primary.hasAnyGetterAnnotation(am) || _secondary.hasAnyGetterAnnotation(am);
+    }
+
+    // // // Deserialization: general annotations
+
+    @Override
+    public Object findDeserializer(Annotated a) {
+        Object r = _primary.findDeserializer(a);
+        if (_isExplicitClassOrOb(r, JsonDeserializer.None.class)) {
+            return r;
+        }
+        return _explicitClassOrOb(_secondary.findDeserializer(a),
+                JsonDeserializer.None.class);
+    }
+
+    @Override
+    public Object findKeyDeserializer(Annotated a) {
+        Object r = _primary.findKeyDeserializer(a);
+        if (_isExplicitClassOrOb(r, KeyDeserializer.None.class)) {
+            return r;
+        }
+        return _explicitClassOrOb(_secondary.findKeyDeserializer(a),
+                KeyDeserializer.None.class);
     }
 
     @Override
     public Object findContentDeserializer(Annotated am) {
         Object r = _primary.findContentDeserializer(am);
-        return _isExplicitClassOrOb(r, JsonDeserializer.None.class)
-                ? r : _secondary.findContentDeserializer(am);
+        if (_isExplicitClassOrOb(r, JsonDeserializer.None.class)) {
+            return r;
+        }
+        return _explicitClassOrOb(_secondary.findContentDeserializer(am),
+                JsonDeserializer.None.class);
+                
     }
 
     @Override
@@ -675,7 +737,7 @@
         JsonPOJOBuilder.Value result = _primary.findPOJOBuilderConfig(ac);
         return (result == null) ? _secondary.findPOJOBuilderConfig(ac) : result;
     }
-    
+
     // // // Deserialization: method annotations
 
     @Override
@@ -693,23 +755,41 @@
         }
         return n;
     }
-    
+
     @Override
-    public boolean hasAnySetterAnnotation(AnnotatedMethod am) {
-        return _primary.hasAnySetterAnnotation(am) || _secondary.hasAnySetterAnnotation(am);
+    public Boolean hasAnySetter(Annotated a) {
+        Boolean b = _primary.hasAnySetter(a);
+        if (b == null) {
+            b = _secondary.hasAnySetter(a);
+        }
+        return b;
     }
 
     @Override
-    public boolean hasAnyGetterAnnotation(AnnotatedMethod am) {
-        return _primary.hasAnyGetterAnnotation(am) || _secondary.hasAnyGetterAnnotation(am);
+    public JsonSetter.Value findSetterInfo(Annotated a) {
+        JsonSetter.Value v2 = _secondary.findSetterInfo(a);
+        JsonSetter.Value v1 = _primary.findSetterInfo(a);
+        return (v2 == null) // shouldn't occur but
+            ? v1 : v2.withOverrides(v1);
     }
-    
+
+    @Override // since 2.9
+    public Boolean findMergeInfo(Annotated a) {
+        Boolean b = _primary.findMergeInfo(a);
+        if (b == null) {
+            b = _secondary.findMergeInfo(a);
+        }
+        return b;
+    }
+
     @Override
+    @Deprecated // since 2.9
     public boolean hasCreatorAnnotation(Annotated a) {
         return _primary.hasCreatorAnnotation(a) || _secondary.hasCreatorAnnotation(a);
     }
 
     @Override
+    @Deprecated // since 2.9
     public JsonCreator.Mode findCreatorBinding(Annotated a) {
         JsonCreator.Mode mode = _primary.findCreatorBinding(a);
         if (mode != null) {
@@ -717,15 +797,37 @@
         }
         return _secondary.findCreatorBinding(a);
     }
-    
+
+    @Override
+    public JsonCreator.Mode findCreatorAnnotation(MapperConfig<?> config, Annotated a) {
+        JsonCreator.Mode mode = _primary.findCreatorAnnotation(config, a);
+        return (mode == null) ? _secondary.findCreatorAnnotation(config, a) : mode;
+    }
+
+    @Override
+    @Deprecated // since 2.9
+    public boolean hasAnySetterAnnotation(AnnotatedMethod am) {
+        return _primary.hasAnySetterAnnotation(am) || _secondary.hasAnySetterAnnotation(am);
+    }
+
     protected boolean _isExplicitClassOrOb(Object maybeCls, Class<?> implicit) {
-        if (maybeCls == null) {
+        if ((maybeCls == null) || (maybeCls == implicit)) {
             return false;
         }
-        if (!(maybeCls instanceof Class<?>)) {
-            return true;
+        if (maybeCls instanceof Class<?>) {
+            return !ClassUtil.isBogusClass((Class<?>) maybeCls);
         }
-        Class<?> cls = (Class<?>) maybeCls;
-        return (cls != implicit && !ClassUtil.isBogusClass(cls));
+        return true;
+    }
+
+    // @since 2.9
+    protected Object _explicitClassOrOb(Object maybeCls, Class<?> implicit) {
+        if ((maybeCls == null) || (maybeCls == implicit)) {
+            return null;
+        }
+        if ((maybeCls instanceof Class<?>) && ClassUtil.isBogusClass((Class<?>) maybeCls)) {
+            return null;
+        }
+        return maybeCls;
     }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationMap.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationMap.java
index 26b31cd..8ae39b6 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationMap.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationMap.java
@@ -16,11 +16,23 @@
     protected HashMap<Class<?>,Annotation> _annotations;
 
     public AnnotationMap() { }
-    
-    private AnnotationMap(HashMap<Class<?>,Annotation> a) {
+
+    public static AnnotationMap of(Class<?> type, Annotation value) {
+        HashMap<Class<?>,Annotation> ann = new HashMap<>(4);
+        ann.put(type, value);
+        return new AnnotationMap(ann);
+    }
+
+    AnnotationMap(HashMap<Class<?>,Annotation> a) {
         _annotations = a;
     }
 
+    /*
+    /**********************************************************
+    /* Annotations impl
+    /**********************************************************
+     */
+    
     @SuppressWarnings("unchecked")
     @Override
     public <A extends Annotation> A get(Class<A> cls)
@@ -31,6 +43,7 @@
         return (A) _annotations.get(cls);
     }
 
+    @Override
     public boolean has(Class<?> cls)
     {
         if (_annotations == null) {
@@ -45,6 +58,7 @@
      *
      * @since 2.7
      */
+    @Override
     public boolean hasOneOf(Class<? extends Annotation>[] annoClasses) {
         if (_annotations != null) {
             for (int i = 0, end = annoClasses.length; i < end; ++i) {
@@ -56,6 +70,12 @@
         return false;
     }
 
+    /*
+    /**********************************************************
+    /* Other API
+    /**********************************************************
+     */
+    
     /**
      * @since 2.3
      */
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/BasicBeanDescription.java b/src/main/java/com/fasterxml/jackson/databind/introspect/BasicBeanDescription.java
index fd2f0d4..4b7cc67 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/BasicBeanDescription.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/BasicBeanDescription.java
@@ -4,6 +4,7 @@
 import java.lang.reflect.Method;
 import java.util.*;
 
+import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.annotation.JsonInclude;
 
@@ -26,6 +27,9 @@
  */
 public class BasicBeanDescription extends BeanDescription
 {
+    // since 2.9
+    private final static Class<?>[] NO_VIEWS = new Class<?>[0];
+
     /*
     /**********************************************************
     /* General configuration
@@ -42,12 +46,28 @@
     final protected MapperConfig<?> _config;
 
     final protected AnnotationIntrospector _annotationIntrospector;
+
+    /*
+    /**********************************************************
+    /* Information about type itself
+    /**********************************************************
+     */
     
     /**
      * Information collected about the class introspected.
      */
     final protected AnnotatedClass _classInfo;
 
+    /**
+     * @since 2.9
+     */
+    protected Class<?>[] _defaultViews;
+
+    /**
+     * @since 2.9
+     */
+    protected boolean _defaultViewsResolved;
+
     /*
     /**********************************************************
     /* Member information
@@ -220,11 +240,18 @@
     }
 
     @Override
+    @Deprecated // since 2.9
     public AnnotatedMethod findJsonValueMethod() {
         return (_propCollector == null) ? null
                 : _propCollector.getJsonValueMethod();
     }
 
+    @Override // since 2.9
+    public AnnotatedMember findJsonValueAccessor() {
+        return (_propCollector == null) ? null
+                : _propCollector.getJsonValueAccessor();
+    }
+ 
     @Override
     public Set<String> getIgnoredPropertyNames() {
         Set<String> ign = (_propCollector == null) ? null
@@ -266,25 +293,39 @@
     }
 
     @Override
-    public AnnotatedMethod findAnySetter() throws IllegalArgumentException
+    public AnnotatedMember findAnySetterAccessor() throws IllegalArgumentException
     {
-        AnnotatedMethod anySetter = (_propCollector == null) ? null
-                : _propCollector.getAnySetterMethod();
-        if (anySetter != null) {
-            /* Also, let's be somewhat strict on how field name is to be
-             * passed; String, Object make sense, others not
-             * so much.
-             */
-            /* !!! 18-May-2009, tatu: how about enums? Can add support if
-             *  requested; easy enough for devs to add support within
-             *  method.
-             */
-            Class<?> type = anySetter.getRawParameterType(0);
-            if (type != String.class && type != Object.class) {
-                throw new IllegalArgumentException("Invalid 'any-setter' annotation on method "+anySetter.getName()+"(): first argument not of type String or Object, but "+type.getName());
+        if (_propCollector != null) {
+            AnnotatedMethod anyMethod = _propCollector.getAnySetterMethod();
+            if (anyMethod != null) {
+                // Also, let's be somewhat strict on how field name is to be
+                // passed; String, Object make sense, others not so much.
+    
+                /* !!! 18-May-2009, tatu: how about enums? Can add support if
+                 *  requested; easy enough for devs to add support within method.
+                 */
+                Class<?> type = anyMethod.getRawParameterType(0);
+                if ((type != String.class) && (type != Object.class)) {
+                    throw new IllegalArgumentException(String.format(
+"Invalid 'any-setter' annotation on method '%s()': first argument not of type String or Object, but %s",
+anyMethod.getName(), type.getName()));
+                }
+                return anyMethod;
+            }
+            AnnotatedMember anyField = _propCollector.getAnySetterField();
+            if (anyField != null) {
+                // For now let's require a Map; in future can add support for other
+                // types like perhaps Iterable<Map.Entry>?
+                Class<?> type = anyField.getRawType();
+                if (!Map.class.isAssignableFrom(type)) {
+                    throw new IllegalArgumentException(String.format(
+"Invalid 'any-setter' annotation on field '%s': type is not instance of java.util.Map",
+anyField.getName()));
+                }
+                return anyField;
             }
         }
-        return anySetter;
+        return null;
     }
 
     @Override
@@ -316,8 +357,8 @@
             while (t.getCause() != null) {
                 t = t.getCause();
             }
-            if (t instanceof Error) throw (Error) t;
-            if (t instanceof RuntimeException) throw (RuntimeException) t;
+            ClassUtil.throwIfError(t);
+            ClassUtil.throwIfRTE(t);
             throw new IllegalArgumentException("Failed to instantiate bean of type "+_classInfo.getAnnotated().getName()+": ("+t.getClass().getName()+") "+t.getMessage(), t);
         }
     }
@@ -364,7 +405,25 @@
         }
         return defValue;
     }
-    
+
+    @Override // since 2.9
+    public Class<?>[] findDefaultViews()
+    {
+        if (!_defaultViewsResolved) {
+            _defaultViewsResolved = true;
+            Class<?>[] def = (_annotationIntrospector == null) ? null
+                    : _annotationIntrospector.findViews(_classInfo);
+            // one more twist: if default inclusion disabled, need to force empty set of views
+            if (def == null) {
+                if (!_config.isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION)) {
+                    def = NO_VIEWS;
+                }
+            }
+            _defaultViews = def;
+        }
+        return _defaultViews;
+    }
+
     /*
     /**********************************************************
     /* Introspection for serialization
@@ -421,54 +480,41 @@
     }
 
     @Override
-    public AnnotatedMember findAnySetterField() throws IllegalArgumentException {
-        AnnotatedMember anySetter = (_propCollector == null) ? null : _propCollector.getAnySetterField();
-		if (anySetter != null) {
-			/*
-			 * For now let's require a Map; in future can add support for other
-			 * types like perhaps Iterable<Map.Entry>?
-			 */
-			Class<?> type = anySetter.getRawType();
-			if (!Map.class.isAssignableFrom(type)) {
-				throw new IllegalArgumentException("Invalid 'any-setter' annotation on field " + anySetter.getName()
-				        + "(): type is not instance of java.util.Map");
-			}
-		}
-		return anySetter;
-	}
-
-    @Override
-    public Map<String,AnnotatedMember> findBackReferenceProperties()
+    public List<BeanPropertyDefinition> findBackReferences()
     {
-        HashMap<String,AnnotatedMember> result = null;
-//        boolean hasIgnored = (_ignoredPropertyNames != null);
-
+        List<BeanPropertyDefinition> result = null;
+        HashSet<String> names = null;
         for (BeanPropertyDefinition property : _properties()) {
-            /* 23-Sep-2014, tatu: As per [databind#426], we _should_ try to avoid
-             *   calling accessor, as it triggers exception from seeming conflict.
-             *   But the problem is that _ignoredPropertyNames here only contains
-             *   ones ignored on per-property annotations, but NOT class annotations...
-             *   so commented out part does not work, alas
-             */
-            /*
-            if (hasIgnored && _ignoredPropertyNames.contains(property.getName())) {
+            AnnotationIntrospector.ReferenceProperty refDef = property.findReferenceType();
+            if ((refDef == null) || !refDef.isBackReference()) {
                 continue;
             }
-            */
-            AnnotatedMember am = property.getMutator();
-            if (am == null) {
-                continue;
-            }
-            AnnotationIntrospector.ReferenceProperty refDef = _annotationIntrospector.findReferenceType(am);
-            if (refDef != null && refDef.isBackReference()) {
-                if (result == null) {
-                    result = new HashMap<String,AnnotatedMember>();
-                }
-                String refName = refDef.getName();
-                if (result.put(refName, am) != null) {
+            final String refName = refDef.getName();
+            if (result == null) {
+                result = new ArrayList<BeanPropertyDefinition>();
+                names = new HashSet<>();
+                names.add(refName);
+            } else {
+                if (!names.add(refName)) {
                     throw new IllegalArgumentException("Multiple back-reference properties with name '"+refName+"'");
                 }
             }
+            result.add(property);
+        }
+        return result;
+    }
+
+    @Deprecated // since 2.9
+    @Override
+    public Map<String,AnnotatedMember> findBackReferenceProperties()
+    {
+        List<BeanPropertyDefinition> props = findBackReferences();
+        if (props == null) {
+            return null;
+        }
+        Map<String,AnnotatedMember> result = new HashMap<>();
+        for (BeanPropertyDefinition prop : props) {
+            result.put(prop.getName(), prop.getMutator());
         }
         return result;
     }
@@ -483,16 +529,22 @@
     public List<AnnotatedMethod> getFactoryMethods()
     {
         // must filter out anything that clearly is not a factory method
-        List<AnnotatedMethod> candidates = _classInfo.getStaticMethods();
+        List<AnnotatedMethod> candidates = _classInfo.getFactoryMethods();
         if (candidates.isEmpty()) {
             return candidates;
         }
-        ArrayList<AnnotatedMethod> result = new ArrayList<AnnotatedMethod>();
+        List<AnnotatedMethod> result = null;
         for (AnnotatedMethod am : candidates) {
             if (isFactoryMethod(am)) {
+                if (result == null) {
+                    result = new ArrayList<AnnotatedMethod>();
+                }
                 result.add(am);
             }
         }
+        if (result == null) {
+            return Collections.emptyList();
+        }
         return result;
     }
 
@@ -520,7 +572,7 @@
     public Method findFactoryMethod(Class<?>... expArgTypes)
     {
         // So, of all single-arg static methods:
-        for (AnnotatedMethod am : _classInfo.getStaticMethods()) {
+        for (AnnotatedMethod am : _classInfo.getFactoryMethods()) {
             // 24-Oct-2016, tatu: Better ensure it only takes 1 arg, no matter what
             if (isFactoryMethod(am) && am.getParameterCount() == 1) {
                 // And must take one of expected arg types (or supertype)
@@ -538,9 +590,8 @@
 
     protected boolean isFactoryMethod(AnnotatedMethod am)
     {
-        /* First: return type must be compatible with the introspected class
-         * (i.e. allowed to be sub-class, although usually is the same class)
-         */
+        // First: return type must be compatible with the introspected class
+        // (i.e. allowed to be sub-class, although usually is the same class)
         Class<?> rt = am.getRawReturnType();
         if (!getBeanClass().isAssignableFrom(rt)) {
             return false;
@@ -549,7 +600,8 @@
          * (a) marked with @JsonCreator annotation, or
          * (b) "valueOf" (at this point, need not be public)
          */
-        if (_annotationIntrospector.hasCreatorAnnotation(am)) {
+        JsonCreator.Mode mode = _annotationIntrospector.findCreatorAnnotation(_config, am);
+        if ((mode != null) && (mode != JsonCreator.Mode.DISABLED)) {
             return true;
         }
         final String name = am.getName();
@@ -666,7 +718,7 @@
      */
     
     @SuppressWarnings("unchecked")
-    public Converter<Object,Object> _createConverter(Object converterDef)
+    protected Converter<Object,Object> _createConverter(Object converterDef)
     {
         if (converterDef == null) {
             return null;
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/BasicClassIntrospector.java b/src/main/java/com/fasterxml/jackson/databind/introspect/BasicClassIntrospector.java
index 7d83043..f511efb 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/BasicClassIntrospector.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/BasicClassIntrospector.java
@@ -24,29 +24,28 @@
      * This is strictly performance optimization to reduce what is
      * usually one-time cost, but seems useful for some cases considering
      * simplicity.
-     * 
+     *
      * @since 2.4
      */
-    
     protected final static BasicBeanDescription STRING_DESC;
     static {
-        AnnotatedClass ac = AnnotatedClass.constructWithoutSuperTypes(String.class, null);
-        STRING_DESC = BasicBeanDescription.forOtherUse(null, SimpleType.constructUnsafe(String.class), ac);
+        STRING_DESC = BasicBeanDescription.forOtherUse(null, SimpleType.constructUnsafe(String.class),
+                AnnotatedClassResolver.createPrimordial(String.class));
     }
     protected final static BasicBeanDescription BOOLEAN_DESC;
     static {
-        AnnotatedClass ac = AnnotatedClass.constructWithoutSuperTypes(Boolean.TYPE, null);
-        BOOLEAN_DESC = BasicBeanDescription.forOtherUse(null, SimpleType.constructUnsafe(Boolean.TYPE), ac);
+        BOOLEAN_DESC = BasicBeanDescription.forOtherUse(null, SimpleType.constructUnsafe(Boolean.TYPE),
+                AnnotatedClassResolver.createPrimordial(Boolean.TYPE));
     }
     protected final static BasicBeanDescription INT_DESC;
     static {
-        AnnotatedClass ac = AnnotatedClass.constructWithoutSuperTypes(Integer.TYPE, null);
-        INT_DESC = BasicBeanDescription.forOtherUse(null, SimpleType.constructUnsafe(Integer.TYPE), ac);
+        INT_DESC = BasicBeanDescription.forOtherUse(null, SimpleType.constructUnsafe(Integer.TYPE),
+                AnnotatedClassResolver.createPrimordial(Integer.TYPE));
     }
     protected final static BasicBeanDescription LONG_DESC;
     static {
-        AnnotatedClass ac = AnnotatedClass.constructWithoutSuperTypes(Long.TYPE, null);
-        LONG_DESC = BasicBeanDescription.forOtherUse(null, SimpleType.constructUnsafe(Long.TYPE), ac);
+        LONG_DESC = BasicBeanDescription.forOtherUse(null, SimpleType.constructUnsafe(Long.TYPE),
+                AnnotatedClassResolver.createPrimordial(Long.TYPE));
     }
 
     /*
@@ -55,9 +54,6 @@
     /**********************************************************
      */
 
-    @Deprecated // since 2.5: construct instance directly
-    public final static BasicClassIntrospector instance = new BasicClassIntrospector();
-
     /**
      * Looks like 'forClassAnnotations()' gets called so frequently that we
      * should consider caching to avoid some of the lookups.
@@ -70,7 +66,12 @@
         // a small cache should go a long way here
         _cachedFCA = new LRUMap<JavaType,BasicBeanDescription>(16, 64);
     }
-    
+
+    @Override
+    public ClassIntrospector copy() {
+        return new BasicClassIntrospector();
+    }
+
     /*
     /**********************************************************
     /* Factory method impls
@@ -84,7 +85,7 @@
         // minor optimization: for some JDK types do minimal introspection
         BasicBeanDescription desc = _findStdTypeDesc(type);
         if (desc == null) {
-            // As per [Databind#550], skip full introspection for some of standard
+            // As per [databind#550], skip full introspection for some of standard
             // structured types as well
             desc = _findStdJdkCollectionDesc(cfg, type);
             if (desc == null) {
@@ -157,8 +158,8 @@
         if (desc == null) {
             desc = _cachedFCA.get(type);
             if (desc == null) {
-                AnnotatedClass ac = AnnotatedClass.construct(type, config, r);
-                desc = BasicBeanDescription.forOtherUse(config, type, ac);
+                desc = BasicBeanDescription.forOtherUse(config, type,
+                        _resolveAnnotatedClass(config, type, r));
                 _cachedFCA.put(type, desc);
             }
         }
@@ -171,12 +172,12 @@
     {
         BasicBeanDescription desc = _findStdTypeDesc(type);
         if (desc == null) {
-            AnnotatedClass ac = AnnotatedClass.constructWithoutSuperTypes(type.getRawClass(), config, r);
-            desc = BasicBeanDescription.forOtherUse(config, type, ac);
+            desc = BasicBeanDescription.forOtherUse(config, type,
+                    _resolveAnnotatedWithoutSuperTypes(config, type, r));
         }
         return desc;
     }
-    
+
     /*
     /**********************************************************
     /* Overridable helper methods
@@ -187,18 +188,18 @@
             JavaType type, MixInResolver r, boolean forSerialization,
             String mutatorPrefix)
     {
-        AnnotatedClass ac = AnnotatedClass.construct(type, config, r);
-        return constructPropertyCollector(config, ac, type, forSerialization, mutatorPrefix);
+        return constructPropertyCollector(config,
+                _resolveAnnotatedClass(config, type, r),
+                type, forSerialization, mutatorPrefix);
     }
-    
+
     protected POJOPropertiesCollector collectPropertiesWithBuilder(MapperConfig<?> config,
             JavaType type, MixInResolver r, boolean forSerialization)
     {
-        boolean useAnnotations = config.isAnnotationProcessingEnabled();
-        AnnotationIntrospector ai = useAnnotations ? config.getAnnotationIntrospector() : null;
-        AnnotatedClass ac = AnnotatedClass.construct(type, config, r);
+        AnnotatedClass ac = _resolveAnnotatedClass(config, type, r);
+        AnnotationIntrospector ai = config.isAnnotationProcessingEnabled() ? config.getAnnotationIntrospector() : null;
         JsonPOJOBuilder.Value builderConfig = (ai == null) ? null : ai.findPOJOBuilderConfig(ac);
-        String mutatorPrefix = (builderConfig == null) ? "with" : builderConfig.withPrefix;
+        String mutatorPrefix = (builderConfig == null) ? JsonPOJOBuilder.DEFAULT_WITH_PREFIX : builderConfig.withPrefix;
         return constructPropertyCollector(config, ac, type, forSerialization, mutatorPrefix);
     }
 
@@ -211,7 +212,7 @@
     {
         return new POJOPropertiesCollector(config, forSerialization, type, ac, mutatorPrefix);
     }
-    
+
     /**
      * Method called to see if type is one of core JDK types
      * that we have cached for efficiency.
@@ -267,9 +268,25 @@
     protected BasicBeanDescription _findStdJdkCollectionDesc(MapperConfig<?> cfg, JavaType type)
     {
         if (_isStdJDKCollection(type)) {
-            AnnotatedClass ac = AnnotatedClass.construct(type, cfg);
-            return BasicBeanDescription.forOtherUse(cfg, type, ac);
+            return BasicBeanDescription.forOtherUse(cfg, type,
+                    _resolveAnnotatedClass(cfg, type, cfg));
         }
         return null;
     }
+
+    /**
+     * @since 2.9
+     */
+    protected AnnotatedClass _resolveAnnotatedClass(MapperConfig<?> config,
+            JavaType type, MixInResolver r) {
+        return AnnotatedClassResolver.resolve(config, type, r);
+    }
+
+    /**
+     * @since 2.9
+     */
+    protected AnnotatedClass _resolveAnnotatedWithoutSuperTypes(MapperConfig<?> config,
+            JavaType type, MixInResolver r) {
+        return AnnotatedClassResolver.resolveWithoutSuperTypes(config, type, r);
+    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/BeanPropertyDefinition.java b/src/main/java/com/fasterxml/jackson/databind/introspect/BeanPropertyDefinition.java
index 4039230..caa2663 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/BeanPropertyDefinition.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/BeanPropertyDefinition.java
@@ -48,7 +48,7 @@
 
     /*
     /**********************************************************
-    /* Basic property information, name, type
+    /* Property name information
     /**********************************************************
      */
 
@@ -83,15 +83,6 @@
     public abstract PropertyName getWrapperName();
 
     /**
-     * Method for accessing additional metadata.
-     * NOTE: will never return null, so de-referencing return value
-     * is safe.
-     * 
-     * @since 2.3
-     */
-    public abstract PropertyMetadata getMetadata();
-    
-    /**
      * Accessor that can be called to check whether property was included
      * due to an explicit marker (usually annotation), or just by naming
      * convention.
@@ -116,7 +107,42 @@
     public boolean isExplicitlyNamed() {
         return isExplicitlyIncluded();
     }
+
+    /*
+    /**********************************************************
+    /* Basic property metadata
+    /**********************************************************
+     */
+
+    /**
+     * @since 2.9
+     */
+    public abstract JavaType getPrimaryType();
+
+    /**
+     * @since 2.9
+     */
+    public abstract Class<?> getRawPrimaryType();
     
+    /**
+     * Method for accessing additional metadata.
+     * NOTE: will never return null, so de-referencing return value
+     * is safe.
+     * 
+     * @since 2.3
+     */
+    public abstract PropertyMetadata getMetadata();
+
+    /**
+     * Method used to check if this property is expected to have a value;
+     * and if none found, should either be considered invalid (and most likely
+     * fail deserialization), or handled by other means (by providing default
+     * value)
+     */
+    public boolean isRequired() {
+        return getMetadata().isRequired();
+    }
+
     /*
     /**********************************************************
     /* Capabilities
@@ -157,20 +183,42 @@
      * value of the property.
      * Null if no such member exists.
      */
-    public abstract AnnotatedMember getAccessor();
+    public AnnotatedMember getAccessor()
+    {
+        AnnotatedMember m = getGetter();
+        if (m == null) {
+            m = getField();
+        }
+        return m;
+    }
 
     /**
      * Method used to find mutator (constructor parameter, setter, field) to use for
      * changing value of the property.
      * Null if no such member exists.
      */
-    public abstract AnnotatedMember getMutator();
+    public AnnotatedMember getMutator() {
+        AnnotatedMember acc = getConstructorParameter();
+        if (acc == null) {
+            acc = getSetter();
+            if (acc == null) {
+                acc = getField();
+            }
+        }
+        return acc;
+    }
 
     /**
      * @since 2.3
      */
-    public abstract AnnotatedMember getNonConstructorMutator();
-    
+    public AnnotatedMember getNonConstructorMutator() {
+        AnnotatedMember m = getSetter();
+        if (m == null) {
+            m = getField();
+        }
+        return m;
+    }
+
     /**
      * Method used to find the property member (getter, setter, field) that has
      * the highest precedence in current context (getter method when serializing,
@@ -185,12 +233,12 @@
     /*
     /**********************************************************
     /* More refined access to configuration features
-    /* (usually based on annotations)
+    /* (usually based on annotations and/or config overrides)
     /* Since most trivial implementations do not support
     /* these methods, they are implemented as no-ops.
     /**********************************************************
      */
-    
+
     /**
      * Method used to find View-inclusion definitions for the property.
      */
@@ -203,6 +251,14 @@
     public AnnotationIntrospector.ReferenceProperty findReferenceType() { return null; }
 
     /**
+     * @since 2.9
+     */
+    public String findReferenceName() {
+        AnnotationIntrospector.ReferenceProperty ref = findReferenceType();
+        return (ref == null) ? null : ref.getName();
+    }
+
+    /**
      * Method used to check whether this logical property has a marker
      * to indicate it should be used as the type id for polymorphic type
      * handling.
@@ -215,17 +271,6 @@
      * (or, when multiple references exist, all but first AS Object Identifier).
      */
     public ObjectIdInfo findObjectIdInfo() { return null; }
-    
-    /**
-     * Method used to check if this property is expected to have a value;
-     * and if none found, should either be considered invalid (and most likely
-     * fail deserialization), or handled by other means (by providing default
-     * value)
-     */
-    public boolean isRequired() {
-        PropertyMetadata md = getMetadata();
-        return (md != null)  && md.isRequired();
-    }
 
     /**
      * Method used to check if this property has specific inclusion override
@@ -235,7 +280,5 @@
      * 
      * @since 2.5
      */
-    public JsonInclude.Value findInclusion() {
-        return EMPTY_INCLUDE;
-    }
+    public abstract JsonInclude.Value findInclusion();
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/ClassIntrospector.java b/src/main/java/com/fasterxml/jackson/databind/introspect/ClassIntrospector.java
index 9bd3f29..6a5dbbf 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/ClassIntrospector.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/ClassIntrospector.java
@@ -46,13 +46,20 @@
     }
 
     protected ClassIntrospector() { }
-	
+
+    /**
+     * Method that may be needed when `copy()`ing `ObjectMapper` instances.
+     *
+     * @since 2.9.6
+     */
+    public abstract ClassIntrospector copy();
+
     /*
     /**********************************************************
     /* Public API: factory methods
     /**********************************************************
      */
-    
+
     /**
      * Factory method that constructs an introspector that has all
      * information needed for serialization purposes.
@@ -100,4 +107,3 @@
     public abstract BeanDescription forDirectClassAnnotations(MapperConfig<?> cfg, JavaType type,
             MixInResolver r);
 }
-
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/CollectorBase.java b/src/main/java/com/fasterxml/jackson/databind/introspect/CollectorBase.java
new file mode 100644
index 0000000..e52dcb4
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/CollectorBase.java
@@ -0,0 +1,123 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import com.fasterxml.jackson.databind.AnnotationIntrospector;
+import com.fasterxml.jackson.databind.util.ClassUtil;
+
+// @since 2.9
+class CollectorBase
+{
+    protected final static AnnotationMap[] NO_ANNOTATION_MAPS = new AnnotationMap[0];
+    protected final static Annotation[] NO_ANNOTATIONS = new Annotation[0];
+
+    protected final AnnotationIntrospector _intr;
+
+    protected CollectorBase(AnnotationIntrospector intr) {
+        _intr = intr;
+    }
+
+    // // // Annotation overrides ("mix over")
+
+    protected final AnnotationCollector collectAnnotations(Annotation[] anns) {
+        AnnotationCollector c = AnnotationCollector.emptyCollector();
+        for (int i = 0, end = anns.length; i < end; ++i) {
+            Annotation ann = anns[i];
+            c = c.addOrOverride(ann);
+            if (_intr.isAnnotationBundle(ann)) {
+                c = collectFromBundle(c, ann);
+            }
+        }
+        return c;
+    }
+
+    protected final AnnotationCollector collectAnnotations(AnnotationCollector c, Annotation[] anns) {
+        for (int i = 0, end = anns.length; i < end; ++i) {
+            Annotation ann = anns[i];
+            c = c.addOrOverride(ann);
+            if (_intr.isAnnotationBundle(ann)) {
+                c = collectFromBundle(c, ann);
+            }
+        }
+        return c;
+    }
+
+    protected final AnnotationCollector collectFromBundle(AnnotationCollector c, Annotation bundle) {
+        Annotation[] anns = ClassUtil.findClassAnnotations(bundle.annotationType());
+        for (int i = 0, end = anns.length; i < end; ++i) {
+            Annotation ann = anns[i];
+            // minor optimization: by-pass 2 common JDK meta-annotations
+            if (_ignorableAnnotation(ann)) {
+                continue;
+            }
+            if (_intr.isAnnotationBundle(ann)) {
+                // 11-Apr-2017, tatu: Also must guard against recursive definitions...
+                if (!c.isPresent(ann)) {
+                    c = c.addOrOverride(ann);
+                    c = collectFromBundle(c, ann);
+                }
+            } else {
+                c = c.addOrOverride(ann);
+            }
+        }
+        return c;
+    }
+
+    // // // Defaulting ("mix under")
+
+    // Variant that only adds annotations that are missing
+    protected final AnnotationCollector collectDefaultAnnotations(AnnotationCollector c,
+            Annotation[] anns) {
+        for (int i = 0, end = anns.length; i < end; ++i) {
+            Annotation ann = anns[i];
+            if (!c.isPresent(ann)) {
+                c = c.addOrOverride(ann);
+                if (_intr.isAnnotationBundle(ann)) {
+                    c = collectDefaultFromBundle(c, ann);
+                }
+            }
+        }
+        return c;
+    }
+
+    protected final AnnotationCollector collectDefaultFromBundle(AnnotationCollector c,
+            Annotation bundle) {
+        Annotation[] anns = ClassUtil.findClassAnnotations(bundle.annotationType());
+        for (int i = 0, end = anns.length; i < end; ++i) {
+            Annotation ann = anns[i];
+            // minor optimization: by-pass 2 common JDK meta-annotations
+            if (_ignorableAnnotation(ann)) {
+                continue;
+            }
+            // also only defaulting, not overrides:
+            if (!c.isPresent(ann)) {
+                c = c.addOrOverride(ann);
+                if (_intr.isAnnotationBundle(ann)) {
+                    c = collectFromBundle(c, ann);
+                }
+            }
+        }
+        return c;
+    }
+    
+    protected final static boolean _ignorableAnnotation(Annotation a) {
+        return (a instanceof Target) || (a instanceof Retention);
+    }
+
+    static AnnotationMap _emptyAnnotationMap() {
+        return new AnnotationMap();
+    }
+
+    static AnnotationMap[] _emptyAnnotationMaps(int count) {
+        if (count == 0) {
+            return NO_ANNOTATION_MAPS;
+        }
+        AnnotationMap[] maps = new AnnotationMap[count];
+        for (int i = 0; i < count; ++i) {
+            maps[i] = _emptyAnnotationMap();
+        }
+        return maps;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/ConcreteBeanPropertyBase.java b/src/main/java/com/fasterxml/jackson/databind/introspect/ConcreteBeanPropertyBase.java
index 7ea742b..7d2bc48 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/ConcreteBeanPropertyBase.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/ConcreteBeanPropertyBase.java
@@ -1,10 +1,14 @@
 package com.fasterxml.jackson.databind.introspect;
 
+import java.util.Collections;
+import java.util.List;
+
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.databind.AnnotationIntrospector;
 import com.fasterxml.jackson.databind.BeanProperty;
 import com.fasterxml.jackson.databind.PropertyMetadata;
+import com.fasterxml.jackson.databind.PropertyName;
 import com.fasterxml.jackson.databind.cfg.MapperConfig;
 
 /**
@@ -24,7 +28,7 @@
      * @since 2.3
      */
     protected final PropertyMetadata _metadata;
-    
+
     /**
      * Lazily accessed value for per-property format override definition.
      * 
@@ -32,6 +36,11 @@
      */
     protected transient JsonFormat.Value _propertyFormat;
 
+    /**
+     * @since 2.9
+     */
+    protected transient List<PropertyName> _aliases;
+
     protected ConcreteBeanPropertyBase(PropertyMetadata md) {
         _metadata = (md == null) ? PropertyMetadata.STD_REQUIRED_OR_OPTIONAL : md;
     }
@@ -95,16 +104,37 @@
     @Override
     public JsonInclude.Value findPropertyInclusion(MapperConfig<?> config, Class<?> baseType)
     {
-        JsonInclude.Value v0 = config.getDefaultPropertyInclusion(baseType);
         AnnotationIntrospector intr = config.getAnnotationIntrospector();
         AnnotatedMember member = getMember();
-        if ((intr == null) || (member == null)) {
+        if (member == null) {
+            JsonInclude.Value def = config.getDefaultPropertyInclusion(baseType);
+            return def;
+        }
+        JsonInclude.Value v0 = config.getDefaultInclusion(baseType, member.getRawType());
+        if (intr == null) {
             return v0;
         }
         JsonInclude.Value v = intr.findPropertyInclusion(member);
-        if (v == null) {
-            return v0;
+        if (v0 == null) {
+            return v;
         }
         return v0.withOverrides(v);
     }
+
+    @Override
+    public List<PropertyName> findAliases(MapperConfig<?> config)
+    {
+        List<PropertyName> aliases = _aliases;
+        if (aliases == null) {
+            AnnotationIntrospector intr = config.getAnnotationIntrospector();
+            if (intr != null) {
+                aliases = intr.findPropertyAliases(getMember());
+            }
+            if (aliases == null) {
+                aliases = Collections.emptyList();
+            }
+            _aliases = aliases;
+        }
+        return aliases;
+    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java b/src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java
index 8417aa3..823deb6 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java
@@ -19,6 +19,8 @@
 import com.fasterxml.jackson.databind.ser.VirtualBeanPropertyWriter;
 import com.fasterxml.jackson.databind.ser.impl.AttributePropertyWriter;
 import com.fasterxml.jackson.databind.ser.std.RawSerializer;
+import com.fasterxml.jackson.databind.type.MapLikeType;
+import com.fasterxml.jackson.databind.type.TypeFactory;
 import com.fasterxml.jackson.databind.util.*;
 
 /**
@@ -53,7 +55,8 @@
         JsonTypeInfo.class,
         JsonUnwrapped.class,
         JsonBackReference.class,
-        JsonManagedReference.class
+        JsonManagedReference.class,
+        JsonMerge.class // since 2.9
     };
 
     // NOTE: loading of Java7 dependencies is encapsulated by handlers in Java7Support,
@@ -172,11 +175,11 @@
      * explicit serialized name
      */
     @Override
-    @Deprecated
+    @Deprecated // since 2.8
     public String findEnumValue(Enum<?> value)
     {
         // 11-Jun-2015, tatu: As per [databind#677], need to allow explicit naming.
-        //   Unfortunately can not quite use standard AnnotatedClass here (due to various
+        //   Unfortunately cannot quite use standard AnnotatedClass here (due to various
         //   reasons, including odd representation JVM uses); has to do for now
         try {
             // We know that values are actually static fields with matching name so:
@@ -270,39 +273,10 @@
     {
         JsonIgnoreProperties v = _findAnnotation(a, JsonIgnoreProperties.class);
         if (v == null) {
-            // could alternatively return `Value.empty()`?
-            return null;
+            return JsonIgnoreProperties.Value.empty();
         }
         return JsonIgnoreProperties.Value.from(v);
     }
-    
-    @Override // since 2.6
-    @Deprecated // since 2.8
-    public String[] findPropertiesToIgnore(Annotated a, boolean forSerialization) {
-        JsonIgnoreProperties.Value v = findPropertyIgnorals(a);
-        if (v == null) {
-            return null;
-        }
-        // 13-May-2015, tatu: As per [databind#95], allow read-only/write-only props
-        if (forSerialization) {
-            if (v.getAllowGetters()) {
-                return null;
-            }
-        } else {
-            if (v.getAllowSetters()) {
-                return null;
-            }
-        }
-        Set<String> ignored = v.getIgnored();
-        return ignored.toArray(new String[ignored.size()]);
-    }
-
-    @Override
-    @Deprecated // since 2.8
-    public Boolean findIgnoreUnknownProperties(AnnotatedClass a) {
-        JsonIgnoreProperties.Value v = findPropertyIgnorals(a);
-        return (v == null) ? null : v.getIgnoreUnknown();
-    }
 
     @Override
     public Boolean isIgnorableType(AnnotatedClass ac) {
@@ -361,7 +335,25 @@
         PropertyName n = _findConstructorName(m);
         return (n == null) ? null : n.getSimpleName();
     }
-    
+
+    @Override
+    public List<PropertyName> findPropertyAliases(Annotated m) {
+        JsonAlias ann = _findAnnotation(m, JsonAlias.class);
+        if (ann == null) {
+            return null;
+        }
+        String[] strs = ann.value();
+        final int len = strs.length;
+        if (len == 0) {
+            return Collections.emptyList();
+        }
+        List<PropertyName> result = new ArrayList<>(len);
+        for (int i = 0; i < len; ++i) {
+            result.add(PropertyName.construct(strs[i]));
+        }
+        return result;
+    }
+
     @Override
     public boolean hasIgnoreMarker(AnnotatedMember m) {
         return _isIgnorable(m);
@@ -449,29 +441,37 @@
         return NameTransformer.simpleTransformer(prefix, suffix);
     }
 
-    @Override
-    public Object findInjectableValueId(AnnotatedMember m)
-    {
+    @Override // since 2.9
+    public JacksonInject.Value findInjectableValue(AnnotatedMember m) {
         JacksonInject ann = _findAnnotation(m, JacksonInject.class);
         if (ann == null) {
             return null;
         }
-        /* Empty String means that we should use name of declared
-         * value class.
-         */
-        String id = ann.value();
-        if (id.length() == 0) {
+        // Empty String means that we should use name of declared value class.
+        JacksonInject.Value v = JacksonInject.Value.from(ann);
+        if (!v.hasId()) {
+            Object id;
             // slight complication; for setters, type 
             if (!(m instanceof AnnotatedMethod)) {
-                return m.getRawType().getName();
+                id = m.getRawType().getName();
+            } else {
+                AnnotatedMethod am = (AnnotatedMethod) m;
+                if (am.getParameterCount() == 0) { // getter
+                    id = m.getRawType().getName();
+                } else { // setter
+                    id = am.getRawParameterType(0).getName();
+                }
             }
-            AnnotatedMethod am = (AnnotatedMethod) m;
-            if (am.getParameterCount() == 0) {
-                return m.getRawType().getName();
-            }
-            return am.getRawParameterType(0).getName();
+            v = v.withId(id);
         }
-        return id;
+        return v;
+    }
+
+    @Override
+    @Deprecated // since 2.9
+    public Object findInjectableValueId(AnnotatedMember m) {
+        JacksonInject.Value v = findInjectableValue(m);
+        return (v == null) ? null : v.getId();
     }
 
     @Override
@@ -678,106 +678,39 @@
     }
 
     @Override
-    @SuppressWarnings("deprecation")
-    public JsonInclude.Include findSerializationInclusion(Annotated a, JsonInclude.Include defValue)
-    {
-        JsonInclude inc = _findAnnotation(a, JsonInclude.class);
-        if (inc != null) {
-            JsonInclude.Include v = inc.value();
-            if (v != JsonInclude.Include.USE_DEFAULTS) {
-                return v;
-            }
-        }
-        JsonSerialize ann = _findAnnotation(a, JsonSerialize.class);
-        if (ann != null) {
-            JsonSerialize.Inclusion i2 = ann.include();
-            switch (i2) {
-            case ALWAYS:
-                return JsonInclude.Include.ALWAYS;
-            case NON_NULL:
-                return JsonInclude.Include.NON_NULL;
-            case NON_DEFAULT:
-                return JsonInclude.Include.NON_DEFAULT;
-            case NON_EMPTY:
-                return JsonInclude.Include.NON_EMPTY;
-            case DEFAULT_INCLUSION: // since 2.3 -- fall through, use default
-                break;
-            }
-        }
-        return defValue;
-    }
-
-    @Override
-    @Deprecated
-    public JsonInclude.Include findSerializationInclusionForContent(Annotated a, JsonInclude.Include defValue)
-    {
-        JsonInclude inc = _findAnnotation(a, JsonInclude.class);
-        if (inc != null) {
-            JsonInclude.Include incl = inc.content();
-            if (incl != JsonInclude.Include.USE_DEFAULTS) {
-                return incl;
-            }
-        }
-        return defValue;
-    }
-
-    @Override
-    @SuppressWarnings("deprecation")
     public JsonInclude.Value findPropertyInclusion(Annotated a)
     {
         JsonInclude inc = _findAnnotation(a, JsonInclude.class);
-        JsonInclude.Include valueIncl = (inc == null) ? JsonInclude.Include.USE_DEFAULTS : inc.value();
-        if (valueIncl == JsonInclude.Include.USE_DEFAULTS) {
-            JsonSerialize ann = _findAnnotation(a, JsonSerialize.class);
-            if (ann != null) {
-                JsonSerialize.Inclusion i2 = ann.include();
-                switch (i2) {
-                case ALWAYS:
-                    valueIncl = JsonInclude.Include.ALWAYS;
-                    break;
-                case NON_NULL:
-                    valueIncl = JsonInclude.Include.NON_NULL;
-                    break;
-                case NON_DEFAULT:
-                    valueIncl = JsonInclude.Include.NON_DEFAULT;
-                    break;
-                case NON_EMPTY:
-                    valueIncl = JsonInclude.Include.NON_EMPTY;
-                    break;
-                case DEFAULT_INCLUSION:
-                default:
-                }
+        JsonInclude.Value value = (inc == null) ? JsonInclude.Value.empty() : JsonInclude.Value.from(inc);
+
+        // only consider deprecated variant if we didn't have non-deprecated one:
+        if (value.getValueInclusion() == JsonInclude.Include.USE_DEFAULTS) {
+            value = _refinePropertyInclusion(a, value);
+        }
+        return value;
+    }
+
+    @SuppressWarnings("deprecation")
+    private JsonInclude.Value _refinePropertyInclusion(Annotated a, JsonInclude.Value value) {
+        JsonSerialize ann = _findAnnotation(a, JsonSerialize.class);
+        if (ann != null) {
+            switch (ann.include()) {
+            case ALWAYS:
+                return value.withValueInclusion(JsonInclude.Include.ALWAYS);
+            case NON_NULL:
+                return value.withValueInclusion(JsonInclude.Include.NON_NULL);
+            case NON_DEFAULT:
+                return value.withValueInclusion(JsonInclude.Include.NON_DEFAULT);
+            case NON_EMPTY:
+                return value.withValueInclusion(JsonInclude.Include.NON_EMPTY);
+            case DEFAULT_INCLUSION:
+            default:
             }
         }
-        JsonInclude.Include contentIncl = (inc == null) ? JsonInclude.Include.USE_DEFAULTS : inc.content();
-        return JsonInclude.Value.construct(valueIncl, contentIncl);
+        return value;
     }
 
     @Override
-    @Deprecated
-    public Class<?> findSerializationType(Annotated am)
-    {
-        JsonSerialize ann = _findAnnotation(am, JsonSerialize.class);
-        return (ann == null) ? null : _classIfExplicit(ann.as());
-    }
-
-    @Override
-    @Deprecated
-    public Class<?> findSerializationKeyType(Annotated am, JavaType baseType)
-    {
-        JsonSerialize ann = _findAnnotation(am, JsonSerialize.class);
-        return (ann == null) ? null : _classIfExplicit(ann.keyAs());
-    }
-
-    @Override
-    @Deprecated
-    public Class<?> findSerializationContentType(Annotated am, JavaType baseType)
-    {
-        JsonSerialize ann = _findAnnotation(am, JsonSerialize.class);
-        return (ann == null) ? null : _classIfExplicit(ann.contentAs());
-    }
-    
-    @Override
     public JsonSerialize.Typing findSerializationTyping(Annotated a)
     {
         JsonSerialize ann = _findAnnotation(a, JsonSerialize.class);
@@ -798,6 +731,148 @@
 
     /*
     /**********************************************************
+    /* Serialization: type refinements
+    /**********************************************************
+     */
+    
+    @Override
+    public JavaType refineSerializationType(final MapperConfig<?> config,
+            final Annotated a, final JavaType baseType) throws JsonMappingException
+    {
+        JavaType type = baseType;
+        final TypeFactory tf = config.getTypeFactory();
+
+        final JsonSerialize jsonSer = _findAnnotation(a, JsonSerialize.class);
+        
+        // Ok: start by refining the main type itself; common to all types
+
+        final Class<?> serClass = (jsonSer == null) ? null : _classIfExplicit(jsonSer.as());
+        if (serClass != null) {
+            if (type.hasRawClass(serClass)) {
+                // 30-Nov-2015, tatu: As per [databind#1023], need to allow forcing of
+                //    static typing this way
+                type = type.withStaticTyping();
+            } else {
+                Class<?> currRaw = type.getRawClass();
+                try {
+                    // 11-Oct-2015, tatu: For deser, we call `TypeFactory.constructSpecializedType()`,
+                    //   may be needed here too in future?
+                    if (serClass.isAssignableFrom(currRaw)) { // common case
+                        type = tf.constructGeneralizedType(type, serClass);
+                    } else if (currRaw.isAssignableFrom(serClass)) { // specialization, ok as well
+                        type = tf.constructSpecializedType(type, serClass);
+                    } else if (_primitiveAndWrapper(currRaw, serClass)) {
+                        // 27-Apr-2017, tatu: [databind#1592] ignore primitive<->wrapper refinements
+                        type = type.withStaticTyping();
+                    } else {
+                        throw new JsonMappingException(null,
+                                String.format("Cannot refine serialization type %s into %s; types not related",
+                                        type, serClass.getName()));
+                    }
+                } catch (IllegalArgumentException iae) {
+                    throw new JsonMappingException(null,
+                            String.format("Failed to widen type %s with annotation (value %s), from '%s': %s",
+                                    type, serClass.getName(), a.getName(), iae.getMessage()),
+                                    iae);
+                }
+            }
+        }
+        // Then further processing for container types
+
+        // First, key type (for Maps, Map-like types):
+        if (type.isMapLikeType()) {
+            JavaType keyType = type.getKeyType();
+            final Class<?> keyClass = (jsonSer == null) ? null : _classIfExplicit(jsonSer.keyAs());
+            if (keyClass != null) {
+                if (keyType.hasRawClass(keyClass)) {
+                    keyType = keyType.withStaticTyping();
+                } else {
+                    Class<?> currRaw = keyType.getRawClass();
+                    try {
+                        // 19-May-2016, tatu: As per [databind#1231], [databind#1178] may need to actually
+                        //   specialize (narrow) type sometimes, even if more commonly opposite
+                        //   is needed.
+                        if (keyClass.isAssignableFrom(currRaw)) { // common case
+                            keyType = tf.constructGeneralizedType(keyType, keyClass);
+                        } else if (currRaw.isAssignableFrom(keyClass)) { // specialization, ok as well
+                            keyType = tf.constructSpecializedType(keyType, keyClass);
+                        } else if (_primitiveAndWrapper(currRaw, keyClass)) {
+                            // 27-Apr-2017, tatu: [databind#1592] ignore primitive<->wrapper refinements
+                            keyType = keyType.withStaticTyping();
+                        } else {
+                            throw new JsonMappingException(null,
+                                    String.format("Cannot refine serialization key type %s into %s; types not related",
+                                            keyType, keyClass.getName()));
+                        }
+                    } catch (IllegalArgumentException iae) {
+                        throw new JsonMappingException(null,
+                                String.format("Failed to widen key type of %s with concrete-type annotation (value %s), from '%s': %s",
+                                        type, keyClass.getName(), a.getName(), iae.getMessage()),
+                                        iae);
+                    }
+                }
+                type = ((MapLikeType) type).withKeyType(keyType);
+            }
+        }
+
+        JavaType contentType = type.getContentType();
+        if (contentType != null) { // collection[like], map[like], array, reference
+            // And then value types for all containers:
+           final Class<?> contentClass = (jsonSer == null) ? null : _classIfExplicit(jsonSer.contentAs());
+           if (contentClass != null) {
+               if (contentType.hasRawClass(contentClass)) {
+                   contentType = contentType.withStaticTyping();
+               } else {
+                   // 03-Apr-2016, tatu: As per [databind#1178], may need to actually
+                   //   specialize (narrow) type sometimes, even if more commonly opposite
+                   //   is needed.
+                   Class<?> currRaw = contentType.getRawClass();
+                   try {
+                       if (contentClass.isAssignableFrom(currRaw)) { // common case
+                           contentType = tf.constructGeneralizedType(contentType, contentClass);
+                       } else if (currRaw.isAssignableFrom(contentClass)) { // specialization, ok as well
+                           contentType = tf.constructSpecializedType(contentType, contentClass);
+                       } else if (_primitiveAndWrapper(currRaw, contentClass)) {
+                           // 27-Apr-2017, tatu: [databind#1592] ignore primitive<->wrapper refinements
+                           contentType = contentType.withStaticTyping();
+                       } else {
+                           throw new JsonMappingException(null,
+                                   String.format("Cannot refine serialization content type %s into %s; types not related",
+                                           contentType, contentClass.getName()));
+                       }
+                   } catch (IllegalArgumentException iae) { // shouldn't really happen
+                       throw new JsonMappingException(null,
+                               String.format("Internal error: failed to refine value type of %s with concrete-type annotation (value %s), from '%s': %s",
+                                       type, contentClass.getName(), a.getName(), iae.getMessage()),
+                                       iae);
+                   }
+               }
+               type = type.withContentType(contentType);
+           }
+        }
+        return type;
+    }
+
+    @Override
+    @Deprecated // since 2.7
+    public Class<?> findSerializationType(Annotated am) {
+        return null;
+    }
+
+    @Override
+    @Deprecated // since 2.7
+    public Class<?> findSerializationKeyType(Annotated am, JavaType baseType) {
+        return null;
+    }
+
+    @Override
+    @Deprecated // since 2.7
+    public Class<?> findSerializationContentType(Annotated am, JavaType baseType) {
+        return null;
+    }
+
+    /*
+    /**********************************************************
     /* Serialization: class annotations
     /**********************************************************
      */
@@ -815,9 +890,8 @@
 
     private final Boolean _findSortAlpha(Annotated ann) {
         JsonPropertyOrder order = _findAnnotation(ann, JsonPropertyOrder.class);
-        /* 23-Jun-2015, tatu: as per [databind#840], let's only consider
-         *  `true` to have any significance.
-         */
+        // 23-Jun-2015, tatu: as per [databind#840], let's only consider
+        //  `true` to have any significance.
         if ((order != null) && order.alphabetic()) {
             return Boolean.TRUE;
         }
@@ -923,25 +997,57 @@
     @Override
     public PropertyName findNameForSerialization(Annotated a)
     {
+        boolean useDefault = false;
         JsonGetter jg = _findAnnotation(a, JsonGetter.class);
         if (jg != null) {
-            return PropertyName.construct(jg.value());
+            String s = jg.value();
+            // 04-May-2018, tatu: Should allow for "nameless" `@JsonGetter` too
+            if (!s.isEmpty()) {
+                return PropertyName.construct(s);
+            }
+            useDefault = true;
         }
         JsonProperty pann = _findAnnotation(a, JsonProperty.class);
         if (pann != null) {
             return PropertyName.construct(pann.value());
         }
-        if (_hasOneOf(a, ANNOTATIONS_TO_INFER_SER)) {
+        if (useDefault || _hasOneOf(a, ANNOTATIONS_TO_INFER_SER)) {
             return PropertyName.USE_DEFAULT;
         }
         return null;
     }
 
+    @Override // since 2.9
+    public Boolean hasAsValue(Annotated a) {
+        JsonValue ann = _findAnnotation(a, JsonValue.class);
+        if (ann == null) {
+            return null;
+        }
+        return ann.value();
+    }
+
+    @Override // since 2.9
+    public Boolean hasAnyGetter(Annotated a) {
+        JsonAnyGetter ann = _findAnnotation(a, JsonAnyGetter.class);
+        if (ann == null) {
+            return null;
+        }
+        return ann.enabled();
+    }
+
     @Override
+    @Deprecated // since 2.9
+    public boolean hasAnyGetterAnnotation(AnnotatedMethod am) {
+        // No dedicated disabling; regular @JsonIgnore used if needs to be ignored (handled separately)
+        return _hasAnnotation(am, JsonAnyGetter.class);
+    }
+
+    @Override
+    @Deprecated // since 2.9
     public boolean hasAsValueAnnotation(AnnotatedMethod am) {
         JsonValue ann = _findAnnotation(am, JsonValue.class);
         // value of 'false' means disabled...
-        return (ann != null && ann.value());
+        return (ann != null) && ann.value();
     }
 
     /*
@@ -1012,33 +1118,90 @@
      */
 
     @Override
-    @Deprecated
-    public Class<?> findDeserializationContentType(Annotated am, JavaType baseContentType)
+    public JavaType refineDeserializationType(final MapperConfig<?> config,
+            final Annotated a, final JavaType baseType) throws JsonMappingException
     {
-        JsonDeserialize ann = _findAnnotation(am, JsonDeserialize.class);
-        return (ann == null) ? null : _classIfExplicit(ann.contentAs());
-    }
-    
-    @Deprecated
-    @Override
-    public Class<?> findDeserializationType(Annotated am, JavaType baseType) {
-        JsonDeserialize ann = _findAnnotation(am, JsonDeserialize.class);
-        return (ann == null) ? null : _classIfExplicit(ann.as());
+        JavaType type = baseType;
+        final TypeFactory tf = config.getTypeFactory();
+
+        final JsonDeserialize jsonDeser = _findAnnotation(a, JsonDeserialize.class);
+        
+        // Ok: start by refining the main type itself; common to all types
+        final Class<?> valueClass = (jsonDeser == null) ? null : _classIfExplicit(jsonDeser.as());
+        if ((valueClass != null) && !type.hasRawClass(valueClass)
+                && !_primitiveAndWrapper(type, valueClass)) {
+            try {
+                type = tf.constructSpecializedType(type, valueClass);
+            } catch (IllegalArgumentException iae) {
+                throw new JsonMappingException(null,
+                        String.format("Failed to narrow type %s with annotation (value %s), from '%s': %s",
+                                type, valueClass.getName(), a.getName(), iae.getMessage()),
+                                iae);
+            }
+        }
+        // Then further processing for container types
+
+        // First, key type (for Maps, Map-like types):
+        if (type.isMapLikeType()) {
+            JavaType keyType = type.getKeyType();
+            final Class<?> keyClass = (jsonDeser == null) ? null : _classIfExplicit(jsonDeser.keyAs());
+            if ((keyClass != null)
+                    && !_primitiveAndWrapper(keyType, keyClass)) {
+                try {
+                    keyType = tf.constructSpecializedType(keyType, keyClass);
+                    type = ((MapLikeType) type).withKeyType(keyType);
+                } catch (IllegalArgumentException iae) {
+                    throw new JsonMappingException(null,
+                            String.format("Failed to narrow key type of %s with concrete-type annotation (value %s), from '%s': %s",
+                                    type, keyClass.getName(), a.getName(), iae.getMessage()),
+                                    iae);
+                }
+            }
+        }
+        JavaType contentType = type.getContentType();
+        if (contentType != null) { // collection[like], map[like], array, reference
+            // And then value types for all containers:
+            final Class<?> contentClass = (jsonDeser == null) ? null : _classIfExplicit(jsonDeser.contentAs());
+            if ((contentClass != null)
+                    && !_primitiveAndWrapper(contentType, contentClass)) {
+                try {
+                    contentType = tf.constructSpecializedType(contentType, contentClass);
+                    type = type.withContentType(contentType);
+                } catch (IllegalArgumentException iae) {
+                    throw new JsonMappingException(null,
+                            String.format("Failed to narrow value type of %s with concrete-type annotation (value %s), from '%s': %s",
+                                    type, contentClass.getName(), a.getName(), iae.getMessage()),
+                            iae);
+                }
+            }
+        }
+        return type;
     }
 
     @Override
-    @Deprecated
-    public Class<?> findDeserializationKeyType(Annotated am, JavaType baseKeyType) {
-        JsonDeserialize ann = _findAnnotation(am, JsonDeserialize.class);
-        return (ann == null) ? null : _classIfExplicit(ann.keyAs());
+    @Deprecated // since 2.7
+    public Class<?> findDeserializationContentType(Annotated am, JavaType baseContentType) {
+        return null;
     }
-    
+
+    @Override
+    @Deprecated // since 2.7
+    public Class<?> findDeserializationType(Annotated am, JavaType baseType) {
+        return null;
+    }
+
+    @Override
+    @Deprecated // since 2.7
+    public Class<?> findDeserializationKeyType(Annotated am, JavaType baseKeyType) {
+        return null;
+    }
+
     /*
     /**********************************************************
     /* Deserialization: Class annotations
     /**********************************************************
      */
-    
+
     @Override
     public Object findValueInstantiator(AnnotatedClass ac)
     {
@@ -1071,41 +1234,53 @@
     public PropertyName findNameForDeserialization(Annotated a)
     {
         // @JsonSetter has precedence over @JsonProperty, being more specific
-        // @JsonDeserialize implies that there is a property, but no name
+
+        boolean useDefault = false;
         JsonSetter js = _findAnnotation(a, JsonSetter.class);
         if (js != null) {
-            return PropertyName.construct(js.value());
+            String s = js.value();
+            // 04-May-2018, tatu: Need to allow for "nameless" `@JsonSetter` too
+            if (s.isEmpty()) {
+                useDefault = true;
+            } else {
+                return PropertyName.construct(s);
+            }
         }
         JsonProperty pann = _findAnnotation(a, JsonProperty.class);
         if (pann != null) {
             return PropertyName.construct(pann.value());
         }
-        if (_hasOneOf(a, ANNOTATIONS_TO_INFER_DESER)) {
+        if (useDefault || _hasOneOf(a, ANNOTATIONS_TO_INFER_DESER)) {
             return PropertyName.USE_DEFAULT;
         }
         return null;
     }
 
     @Override
-    public boolean hasAnySetterAnnotation(AnnotatedMethod am)
-    {
-        /* No dedicated disabling; regular @JsonIgnore used
-         * if needs to be ignored (and if so, is handled prior
-         * to this method getting called)
-         */
+    public Boolean hasAnySetter(Annotated a) {
+        JsonAnySetter ann = _findAnnotation(a, JsonAnySetter.class);
+        return (ann == null) ? null : ann.enabled();
+    }
+
+    @Override
+    public JsonSetter.Value findSetterInfo(Annotated a) {
+        return JsonSetter.Value.from(_findAnnotation(a, JsonSetter.class));
+    }
+
+    @Override // since 2.9
+    public Boolean findMergeInfo(Annotated a) {
+        JsonMerge ann = _findAnnotation(a, JsonMerge.class);
+        return (ann == null) ? null : ann.value().asBoolean();
+    }
+
+    @Override
+    @Deprecated // since 2.9
+    public boolean hasAnySetterAnnotation(AnnotatedMethod am) {
         return _hasAnnotation(am, JsonAnySetter.class);
     }
 
     @Override
-    public boolean hasAnyGetterAnnotation(AnnotatedMethod am)
-    {
-        /* No dedicated disabling; regular @JsonIgnore used
-         * if needs to be ignored (handled separately
-         */
-        return _hasAnnotation(am, JsonAnyGetter.class);
-    }
-
-    @Override
+    @Deprecated // since 2.9
     public boolean hasCreatorAnnotation(Annotated a)
     {
         /* No dedicated disabling; regular @JsonIgnore used if needs to be
@@ -1131,11 +1306,35 @@
     }
 
     @Override
+    @Deprecated // since 2.9
     public JsonCreator.Mode findCreatorBinding(Annotated a) {
         JsonCreator ann = _findAnnotation(a, JsonCreator.class);
         return (ann == null) ? null : ann.mode();
     }
 
+    @Override
+    public JsonCreator.Mode findCreatorAnnotation(MapperConfig<?> config, Annotated a) {
+        JsonCreator ann = _findAnnotation(a, JsonCreator.class);
+        if (ann != null) {
+            return ann.mode();
+        }
+        if (_cfgConstructorPropertiesImpliesCreator
+                && config.isEnabled(MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES)
+            ) {
+            if (a instanceof AnnotatedConstructor) {
+                if (_java7Helper != null) {
+                    Boolean b = _java7Helper.hasCreatorAnnotation(a);
+                    if ((b != null) && b.booleanValue()) {
+                        // 13-Sep-2016, tatu: Judgment call, but I don't think JDK ever implies
+                        //    use of delegate; assumes as-properties implicitly
+                        return JsonCreator.Mode.PROPERTIES;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
     /*
     /**********************************************************
     /* Helper methods
@@ -1252,7 +1451,7 @@
         // 08-Dec-2014, tatu: To deprecate `JsonTypeInfo.None` we need to use other placeholder(s);
         //   and since `java.util.Void` has other purpose (to indicate "deser as null"), we'll instead
         //   use `JsonTypeInfo.class` itself. But any annotation type will actually do, as they have no
-        //   valid use (can not instantiate as default)
+        //   valid use (cannot instantiate as default)
         if (defaultImpl != JsonTypeInfo.None.class && !defaultImpl.isAnnotation()) {
             b = b.defaultImpl(defaultImpl);
         }
@@ -1276,9 +1475,25 @@
         return StdTypeResolverBuilder.noTypeInfoBuilder();
     }
 
-    /*
-    /**********************************************************
-    /* Helper classes
-    /**********************************************************
-     */
+    private boolean _primitiveAndWrapper(Class<?> baseType, Class<?> refinement)
+    {
+        if (baseType.isPrimitive()) {
+            return baseType == ClassUtil.primitiveType(refinement);
+        }
+        if (refinement.isPrimitive()) {
+            return refinement == ClassUtil.primitiveType(baseType);
+        }
+        return false;
+    }
+
+    private boolean _primitiveAndWrapper(JavaType baseType, Class<?> refinement)
+    {
+        if (baseType.isPrimitive()) {
+            return baseType.hasRawClass(ClassUtil.primitiveType(refinement));
+        }
+        if (refinement.isPrimitive()) {
+            return refinement == ClassUtil.primitiveType(baseType.getRawClass());
+        }
+        return false;
+    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/MemberKey.java b/src/main/java/com/fasterxml/jackson/databind/introspect/MemberKey.java
index d44d885..58563b8 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/MemberKey.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/MemberKey.java
@@ -31,14 +31,21 @@
         _argTypes = (argTypes == null) ? NO_CLASSES : argTypes;
     }
 
+    public String getName() {
+        return _name;
+    }
+
+    public int argCount() {
+        return _argTypes.length;
+    }
+
     @Override
     public String toString() {
         return _name + "(" + _argTypes.length+"-args)";
     }
 
     @Override
-    public int hashCode()
-    {
+    public int hashCode() {
         return _name.hashCode() + _argTypes.length;
     }
 
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/ObjectIdInfo.java b/src/main/java/com/fasterxml/jackson/databind/introspect/ObjectIdInfo.java
index 049ab35..0605830 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/ObjectIdInfo.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/ObjectIdInfo.java
@@ -5,6 +5,7 @@
 import com.fasterxml.jackson.annotation.ObjectIdResolver;
 import com.fasterxml.jackson.annotation.SimpleObjectIdResolver;
 import com.fasterxml.jackson.databind.PropertyName;
+import com.fasterxml.jackson.databind.util.ClassUtil;
 
 /**
  * Container object that encapsulates information usually
@@ -30,17 +31,6 @@
         this(name, scope, gen, false, resolver);
     }
 
-    @Deprecated // since 2.4
-    public ObjectIdInfo(PropertyName name, Class<?> scope, Class<? extends ObjectIdGenerator<?>> gen)
-    {
-        this(name, scope, gen, false);
-    }
-
-    @Deprecated // since 2.3
-    public ObjectIdInfo(String name, Class<?> scope, Class<? extends ObjectIdGenerator<?>> gen) {
-        this(new PropertyName(name), scope, gen, false);
-    }
-    
     protected ObjectIdInfo(PropertyName prop, Class<?> scope, Class<? extends ObjectIdGenerator<?>> gen,
             boolean alwaysAsId)
     {
@@ -81,8 +71,8 @@
     @Override
     public String toString() {
         return "ObjectIdInfo: propName="+_propertyName
-                +", scope="+(_scope == null ? "null" : _scope.getName())
-                +", generatorType="+(_generator == null ? "null" : _generator.getName())
+                +", scope="+ClassUtil.nameOf(_scope)
+                +", generatorType="+ClassUtil.nameOf(_generator)
                 +", alwaysAsId="+_alwaysAsId;
     }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java b/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java
index fd6a613..4612cbd 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java
@@ -3,9 +3,12 @@
 import java.lang.reflect.Modifier;
 import java.util.*;
 
-import com.fasterxml.jackson.annotation.JsonAnySetter;
-import com.fasterxml.jackson.annotation.JsonProperty.Access;
+import com.fasterxml.jackson.annotation.JacksonInject;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
 import com.fasterxml.jackson.databind.*;
+
 import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
 import com.fasterxml.jackson.databind.cfg.MapperConfig;
 import com.fasterxml.jackson.databind.util.BeanUtil;
@@ -54,6 +57,11 @@
     protected final AnnotationIntrospector _annotationIntrospector;
 
     /**
+     * @since 2.9
+     */
+    protected final boolean _useAnnotations;
+
+    /**
      * Prefix used by auto-detected mutators ("setters"): usually "set",
      * but differs for builder objects ("with" by default).
      */
@@ -80,7 +88,7 @@
      */
     protected LinkedHashMap<String, POJOPropertyBuilder> _properties;
 
-    protected LinkedList<POJOPropertyBuilder> _creatorProperties ;
+    protected LinkedList<POJOPropertyBuilder> _creatorProperties;
     
     protected LinkedList<AnnotatedMember> _anyGetters;
 
@@ -90,8 +98,10 @@
 
     /**
      * Method(s) marked with 'JsonValue' annotation
+     *<p>
+     * NOTE: before 2.9, was `AnnotatedMethod`; with 2.9 allows fields too
      */
-    protected LinkedList<AnnotatedMethod> _jsonValueGetters;
+    protected LinkedList<AnnotatedMember> _jsonValueAccessors;
 
     /**
      * Lazily collected list of properties that can be implicitly
@@ -122,14 +132,15 @@
         _type = type;
         _classDef = classDef;
         _mutatorPrefix = (mutatorPrefix == null) ? "set" : mutatorPrefix;
-        _annotationIntrospector = config.isAnnotationProcessingEnabled() ?
-                _config.getAnnotationIntrospector() : null;
-        if (_annotationIntrospector == null) {
-            _visibilityChecker = _config.getDefaultVisibilityChecker();
+        if (config.isAnnotationProcessingEnabled()) {
+            _useAnnotations = true;
+            _annotationIntrospector = _config.getAnnotationIntrospector();
         } else {
-            _visibilityChecker = _annotationIntrospector.findAutoDetectVisibility(classDef,
-                    _config.getDefaultVisibilityChecker());
+            _useAnnotations = false;
+            _annotationIntrospector = AnnotationIntrospector.nopInstance();
         }
+        _visibilityChecker = _config.getDefaultVisibilityChecker(type.getRawClass(),
+                classDef);
     }
 
     /*
@@ -166,20 +177,33 @@
         }
         return _injectables;
     }
-    
-    public AnnotatedMethod getJsonValueMethod()
+
+    @Deprecated // since 2.9
+    public AnnotatedMethod getJsonValueMethod() {
+        AnnotatedMember m = getJsonValueAccessor();
+        if (m instanceof AnnotatedMethod) {
+            return (AnnotatedMethod) m;
+        }
+        return null;
+    }
+
+    /**
+     * @since 2.9
+     */
+    public AnnotatedMember getJsonValueAccessor()
     {
         if (!_collected) {
             collectAll();
         }
         // If @JsonValue defined, must have a single one
-        if (_jsonValueGetters != null) {
-            if (_jsonValueGetters.size() > 1) {
-                reportProblem("Multiple value properties defined ("+_jsonValueGetters.get(0)+" vs "
-                        +_jsonValueGetters.get(1)+")");
+        if (_jsonValueAccessors != null) {
+            if (_jsonValueAccessors.size() > 1) {
+                reportProblem("Multiple 'as-value' properties defined (%s vs %s)",
+                        _jsonValueAccessors.get(0),
+                        _jsonValueAccessors.get(1));
             }
             // otherwise we won't greatly care
-            return _jsonValueGetters.get(0);
+            return _jsonValueAccessors.get(0);
         }
         return null;
     }
@@ -191,14 +215,14 @@
         }
         if (_anyGetters != null) {
             if (_anyGetters.size() > 1) {
-                reportProblem("Multiple 'any-getters' defined ("+_anyGetters.get(0)+" vs "
-                        +_anyGetters.get(1)+")");
+                reportProblem("Multiple 'any-getters' defined (%s vs %s)",
+                        _anyGetters.get(0), _anyGetters.get(1));
             }
             return _anyGetters.getFirst();
         }        
         return null;
     }
-    
+
     public AnnotatedMember getAnySetterField()
     {
         if (!_collected) {
@@ -206,8 +230,8 @@
         }
         if (_anySetterField != null) {
             if (_anySetterField.size() > 1) {
-                reportProblem("Multiple 'any-Setters' defined ("+_anySetters.get(0)+" vs "
-                        +_anySetterField.get(1)+")");
+                reportProblem("Multiple 'any-setter' fields defined (%s vs %s)",
+                        _anySetterField.get(0), _anySetterField.get(1));
             }
             return _anySetterField.getFirst();
         }
@@ -221,8 +245,8 @@
         }
         if (_anySetters != null) {
             if (_anySetters.size() > 1) {
-                reportProblem("Multiple 'any-setters' defined ("+_anySetters.get(0)+" vs "
-                        +_anySetters.get(1)+")");
+                reportProblem("Multiple 'any-setter' methods defined (%s vs %s)",
+                        _anySetters.get(0), _anySetters.get(1));
             }
             return _anySetters.getFirst();
         }
@@ -243,9 +267,6 @@
      */
     public ObjectIdInfo getObjectIdInfo()
     {
-        if (_annotationIntrospector == null) {
-            return null;
-        }
         ObjectIdInfo info = _annotationIntrospector.findObjectIdInfo(_classDef);
         if (info != null) { // 2.1: may also have different defaults for refs:
             info = _annotationIntrospector.findObjectReferenceInfo(_classDef, info);
@@ -256,8 +277,7 @@
     /**
      * Method for finding Class to use as POJO builder, if any.
      */
-    public Class<?> findPOJOBuilderClass()
-    {
+    public Class<?> findPOJOBuilderClass() {
         return _annotationIntrospector.findPOJOBuilder(_classDef);
     }
     
@@ -276,20 +296,6 @@
      */
 
     /**
-     * Method that orchestrates collection activities, and needs to be called
-     * after creating the instance.
-     *<p>
-     * Since 2.6 has become a no-op and actual collection is done more lazily
-     * at point where properties are actually needed.
-     * 
-     * @deprecated Since 2.6; no need to call
-     */
-    @Deprecated
-    public POJOPropertiesCollector collect() {
-        return this;
-    }
-
-    /**
      * Internal method that will collect actual property information.
      *
      * @since 2.6
@@ -311,17 +317,19 @@
         // Remove ignored properties, first; this MUST precede annotation merging
         // since logic relies on knowing exactly which accessor has which annotation
         _removeUnwantedProperties(props);
-
-        // then merge annotations, to simplify further processing
-        for (POJOPropertyBuilder property : props.values()) {
-            property.mergeAnnotations(_forSerialization);
-        }
         // and then remove unneeded accessors (wrt read-only, read-write)
         _removeUnwantedAccessor(props);
 
         // Rename remaining properties
         _renameProperties(props);
 
+        // then merge annotations, to simplify further processing
+        // 26-Sep-2017, tatu: Before 2.9.2 was done earlier but that prevented some of
+        //   annotations from getting properly merged
+        for (POJOPropertyBuilder property : props.values()) {
+            property.mergeAnnotations(_forSerialization);
+        }
+
         // And use custom naming strategy, if applicable...
         PropertyNamingStrategy naming = _findNamingStrategy();
         if (naming != null) {
@@ -342,7 +350,7 @@
         if (_config.isEnabled(MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME)) {
             _renameWithWrappers(props);
         }
-        
+
         // well, almost last: there's still ordering...
         _sortProperties(props);
         _properties = props;
@@ -369,16 +377,29 @@
         final boolean transientAsIgnoral = _config.isEnabled(MapperFeature.PROPAGATE_TRANSIENT_MARKER);
         
         for (AnnotatedField f : _classDef.fields()) {
-            String implName = (ai == null) ? null : ai.findImplicitPropertyName(f);
+            String implName = ai.findImplicitPropertyName(f);
+            // @JsonValue?
+            if (Boolean.TRUE.equals(ai.hasAsValue(f))) {
+                if (_jsonValueAccessors == null) {
+                    _jsonValueAccessors = new LinkedList<>();
+                }
+                _jsonValueAccessors.add(f);
+                continue;
+            }
+            // @JsonAnySetter?
+            if (Boolean.TRUE.equals(ai.hasAnySetter(f))) {
+                if (_anySetterField == null) {
+                    _anySetterField = new LinkedList<AnnotatedMember>();
+                }
+                _anySetterField.add(f);
+                continue;
+            }
             if (implName == null) {
                 implName = f.getName();
             }
-
             PropertyName pn;
 
-            if (ai == null) {
-                pn = null;
-            } else if (_forSerialization) {
+            if (_forSerialization) {
                 /* 18-Aug-2011, tatu: As per existing unit tests, we should only
                  *   use serialization annotation (@JsonSerialize) when serializing
                  *   fields, and similarly for deserialize-only annotations... so
@@ -401,7 +422,7 @@
                 visible = _visibilityChecker.isFieldVisible(f);
             }
             // and finally, may also have explicit ignoral
-            boolean ignored = (ai != null) && ai.hasIgnoreMarker(f);
+            boolean ignored = ai.hasIgnoreMarker(f);
 
             // 13-May-2015, tatu: Moved from earlier place (AnnotatedClass) in 2.6
             if (f.isTransient()) {
@@ -419,17 +440,10 @@
              *  Also: if 'ignored' is set, need to included until a later point, to
              *  avoid losing ignoral information.
              */
-            if (pruneFinalFields && (pn == null) && !ignored && Modifier.isFinal(f.getModifiers())) {
+            if (pruneFinalFields && (pn == null) && !ignored
+                    && Modifier.isFinal(f.getModifiers())) {
                 continue;
             }
-
-            //if field has annotation @JsonAnySetter
-            if(f.hasAnnotation(JsonAnySetter.class)) {
-            	if (_anySetterField == null) {
-            		_anySetterField = new LinkedList<AnnotatedMember>();
-            	}
-            	_anySetterField.add(f);
-            }
             _property(props, implName).addField(f, pn, nameExplicit, visible, ignored);
         }
     }
@@ -440,7 +454,7 @@
     protected void _addCreators(Map<String, POJOPropertyBuilder> props)
     {
         // can be null if annotation processing is disabled...
-        if (_annotationIntrospector == null) {
+        if (!_useAnnotations) {
             return;
         }
         for (AnnotatedConstructor ctor : _classDef.getConstructors()) {
@@ -451,7 +465,7 @@
                 _addCreatorParam(props, ctor.getParameter(i));
             }
         }
-        for (AnnotatedMethod factory : _classDef.getStaticMethods()) {
+        for (AnnotatedMethod factory : _classDef.getFactoryMethods()) {
             if (_creatorProperties == null) {
                 _creatorProperties = new LinkedList<POJOPropertyBuilder>();
             }
@@ -476,14 +490,14 @@
         boolean expl = (pn != null && !pn.isEmpty());
         if (!expl) {
             if (impl.isEmpty()) {
-                /* Important: if neither implicit nor explicit name, can not make use
-                 * of this creator parameter -- may or may not be a problem, verified
-                 * at a later point.
-                 */
+                // Important: if neither implicit nor explicit name, cannot make use of
+                // this creator parameter -- may or may not be a problem, verified at a later point.
                 return;
             }
             // Also: if this occurs, there MUST be explicit annotation on creator itself
-            if (!_annotationIntrospector.hasCreatorAnnotation(param.getOwner())) {
+            JsonCreator.Mode creatorMode = _annotationIntrospector.findCreatorAnnotation(_config,
+                    param.getOwner());
+            if ((creatorMode == null) || (creatorMode == JsonCreator.Mode.DISABLED)) {
                 return;
             }
             pn = PropertyName.construct(impl);
@@ -501,14 +515,13 @@
         prop.addCtor(param, pn, expl, true, false);
         _creatorProperties.add(prop);
     }
-    
+
     /**
      * Method for collecting basic information on all fields found
      */
     protected void _addMethods(Map<String, POJOPropertyBuilder> props)
     {
         final AnnotationIntrospector ai = _annotationIntrospector;
-        
         for (AnnotatedMethod m : _classDef.memberMethods()) {
             /* For methods, handling differs between getters and setters; and
              * we will also only consider entries that either follow the bean
@@ -521,11 +534,13 @@
             } else if (argCount == 1) { // setters
             	_addSetterMethod(props, m, ai);
             } else if (argCount == 2) { // any getter?
-                if (ai != null  && ai.hasAnySetterAnnotation(m)) {
-                    if (_anySetters == null) {
-                        _anySetters = new LinkedList<AnnotatedMethod>();
+                if (ai != null) {
+                    if (Boolean.TRUE.equals(ai.hasAnySetter(m))) {
+                        if (_anySetters == null) {
+                            _anySetters = new LinkedList<AnnotatedMethod>();
+                        }
+                        _anySetters.add(m);
                     }
-                    _anySetters.add(m);
                 }
             }
         }
@@ -540,31 +555,30 @@
         }
         
         // any getter?
-        if (ai != null) {
-            if (ai.hasAnyGetterAnnotation(m)) {
-                if (_anyGetters == null) {
-                    _anyGetters = new LinkedList<AnnotatedMember>();
-                }
-                _anyGetters.add(m);
-                return;
+        // @JsonAnyGetter?
+        if (Boolean.TRUE.equals(ai.hasAnyGetter(m))) {
+            if (_anyGetters == null) {
+                _anyGetters = new LinkedList<AnnotatedMember>();
             }
-            // @JsonValue?
-            if (ai.hasAsValueAnnotation(m)) {
-                if (_jsonValueGetters == null) {
-                    _jsonValueGetters = new LinkedList<AnnotatedMethod>();
-                }
-                _jsonValueGetters.add(m);
-                return;
+            _anyGetters.add(m);
+            return;
+        }
+        // @JsonValue?
+        if (Boolean.TRUE.equals(ai.hasAsValue(m))) {
+            if (_jsonValueAccessors == null) {
+                _jsonValueAccessors = new LinkedList<>();
             }
+            _jsonValueAccessors.add(m);
+            return;
         }
         String implName; // from naming convention
         boolean visible;
 
-        PropertyName pn = (ai == null) ? null : ai.findNameForSerialization(m);
+        PropertyName pn = ai.findNameForSerialization(m);
         boolean nameExplicit = (pn != null);
 
         if (!nameExplicit) { // no explicit name; must consider implicit
-            implName = (ai == null) ? null : ai.findImplicitPropertyName(m);
+            implName = ai.findImplicitPropertyName(m);
             if (implName == null) {
                 implName = BeanUtil.okNameForRegularGetter(m, m.getName(), _stdBeanNaming);
             }
@@ -579,7 +593,7 @@
             }
         } else { // explicit indication of inclusion, but may be empty
             // we still need implicit name to link with other pieces
-            implName = (ai == null) ? null : ai.findImplicitPropertyName(m);
+            implName = ai.findImplicitPropertyName(m);
             if (implName == null) {
                 implName = BeanUtil.okNameForGetter(m, _stdBeanNaming);
             }
@@ -594,7 +608,7 @@
             }
             visible = true;
         }
-        boolean ignore = (ai == null) ? false : ai.hasIgnoreMarker(m);
+        boolean ignore = ai.hasIgnoreMarker(m);
         _property(props, implName).addGetter(m, pn, nameExplicit, visible, ignore);
     }
 
@@ -634,43 +648,41 @@
         boolean ignore = (ai == null) ? false : ai.hasIgnoreMarker(m);
         _property(props, implName).addSetter(m, pn, nameExplicit, visible, ignore);
     }
-    
+
     protected void _addInjectables(Map<String, POJOPropertyBuilder> props)
     {
         final AnnotationIntrospector ai = _annotationIntrospector;
-        if (ai == null) {
-            return;
-        }
-        
-        // first fields, then methods
+        // first fields, then methods, to allow overriding
         for (AnnotatedField f : _classDef.fields()) {
-            _doAddInjectable(ai.findInjectableValueId(f), f);
+            _doAddInjectable(ai.findInjectableValue(f), f);
         }
         
         for (AnnotatedMethod m : _classDef.memberMethods()) {
-            /* for now, only allow injection of a single arg
-             * (to be changed in future)
-             */
+            // for now, only allow injection of a single arg (to be changed in future?)
             if (m.getParameterCount() != 1) {
                 continue;
             }
-            _doAddInjectable(ai.findInjectableValueId(m), m);
+            _doAddInjectable(ai.findInjectableValue(m), m);
         }
     }
 
-    protected void _doAddInjectable(Object id, AnnotatedMember m)
+    protected void _doAddInjectable(JacksonInject.Value injectable, AnnotatedMember m)
     {
-        if (id == null) {
+        if (injectable == null) {
             return;
         }
+        Object id = injectable.getId();
         if (_injectables == null) {
             _injectables = new LinkedHashMap<Object, AnnotatedMember>();
         }
         AnnotatedMember prev = _injectables.put(id, m);
         if (prev != null) {
-            String type = id.getClass().getName();
-            throw new IllegalArgumentException("Duplicate injectable value with id '"
-                    +String.valueOf(id)+"' (of type "+type+")");
+            // 12-Apr-2017, tatu: Let's allow masking of Field by Method
+            if (prev.getClass() == m.getClass()) {
+                String type = id.getClass().getName();
+                throw new IllegalArgumentException("Duplicate injectable value with id '"
+                        +String.valueOf(id)+"' (of type "+type+")");
+            }
         }
     }
 
@@ -709,7 +721,7 @@
                 }
                 // otherwise just remove ones marked to be ignored
                 prop.removeIgnored();
-                if (!_forSerialization && !prop.couldDeserialize()) {
+                if (!prop.couldDeserialize()) {
                     _collectIgnorals(prop.getName());
                 }
             }
@@ -729,8 +741,8 @@
         while (it.hasNext()) {
             POJOPropertyBuilder prop = it.next();
             // 26-Jan-2017, tatu: [databind#935]: need to denote removal of
-            Access acc = prop.removeNonVisible(inferMutators);
-            if (!_forSerialization && (acc == Access.READ_ONLY)) {
+            JsonProperty.Access acc = prop.removeNonVisible(inferMutators);
+            if (acc == JsonProperty.Access.READ_ONLY) {
                 _collectIgnorals(prop.getName());
             }
         }
@@ -810,6 +822,12 @@
                 }
                 // replace the creatorProperty too, if there is one
                 _updateCreatorProperty(prop, _creatorProperties);
+                // [databind#2001]: New name of property was ignored previously? Remove from ignored
+                // 01-May-2018, tatu: I have a feeling this will need to be revisited at some point,
+                //   to avoid removing some types of removals, possibly. But will do for now.
+                if (_ignoredPropertyNames != null) {
+                    _ignoredPropertyNames.remove(name);
+                }
             }
         }
     }
@@ -847,21 +865,20 @@
                 }
             }
             final String simpleName;
-            if (rename != null && !fullName.hasSimpleName(rename)) {
+            if ((rename != null) && !fullName.hasSimpleName(rename)) {
                 prop = prop.withSimpleName(rename);
                 simpleName = rename;
             } else {
                 simpleName = fullName.getSimpleName();
             }
-            /* As per [JACKSON-687], need to consider case where there may already be
-             * something in there...
-             */
+            // Need to consider case where there may already be something in there...
             POJOPropertyBuilder old = propMap.get(simpleName);
             if (old == null) {
                 propMap.put(simpleName, prop);
             } else {
                 old.addAll(prop);
             }
+
             // replace the creatorProperty too, if there is one
             _updateCreatorProperty(prop, _creatorProperties);
         }
@@ -869,9 +886,8 @@
 
     protected void _renameWithWrappers(Map<String, POJOPropertyBuilder> props)
     {
-        /* 11-Sep-2012, tatu: To support 'MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME',
-         *   need another round of renaming...
-         */
+        // 11-Sep-2012, tatu: To support 'MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME',
+        //   need another round of renaming...
         Iterator<Map.Entry<String,POJOPropertyBuilder>> it = props.entrySet().iterator();
         LinkedList<POJOPropertyBuilder> renamed = null;
         while (it.hasNext()) {
@@ -882,7 +898,7 @@
                 continue;
             }
             PropertyName wrapperName = _annotationIntrospector.findWrapperName(member);
-            // One trickier part (wrt [Issue#24] of JAXB annotations: wrapper that
+            // One trickier part (wrt [#24] of JAXB annotations: wrapper that
             // indicates use of actual property... But hopefully has been taken care
             // of previously
             if (wrapperName == null || !wrapperName.hasSimpleName()) {
@@ -924,7 +940,7 @@
     {
         // Then how about explicit ordering?
         AnnotationIntrospector intr = _annotationIntrospector;
-        Boolean alpha = (intr == null) ? null : intr.findSerializationSortAlphabetically((Annotated) _classDef);
+        Boolean alpha = intr.findSerializationSortAlphabetically((Annotated) _classDef);
         boolean sort;
         
         if (alpha == null) {
@@ -932,7 +948,7 @@
         } else {
             sort = alpha.booleanValue();
         }
-        String[] propertyOrder = (intr == null) ? null : intr.findSerializationPropertyOrder(_classDef);
+        String[] propertyOrder = intr.findSerializationPropertyOrder(_classDef);
         
         // no sorting? no need to shuffle, then
         if (!sort && (_creatorProperties == null) && (propertyOrder == null)) {
@@ -999,7 +1015,6 @@
         }
         // And finally whatever is left (trying to put again will not change ordering)
         ordered.putAll(all);
-        
         props.clear();
         props.putAll(ordered);
     }        
@@ -1010,13 +1025,23 @@
     /**********************************************************
      */
 
-    protected void reportProblem(String msg) {
+    protected void reportProblem(String msg, Object... args) {
+        if (args.length > 0) {
+            msg = String.format(msg, args);
+        }
         throw new IllegalArgumentException("Problem with definition of "+_classDef+": "+msg);
     }
 
     protected POJOPropertyBuilder _property(Map<String, POJOPropertyBuilder> props,
             PropertyName name) {
-        return _property(props, name.getSimpleName());
+        String simpleName = name.getSimpleName();
+        POJOPropertyBuilder prop = props.get(simpleName);
+        if (prop == null) {
+            prop = new POJOPropertyBuilder(_config, _annotationIntrospector,
+                    _forSerialization, name);
+            props.put(simpleName, prop);
+        }
+        return prop;
     }
     
     // !!! TODO: deprecate, require use of PropertyName
@@ -1034,8 +1059,7 @@
 
     private PropertyNamingStrategy _findNamingStrategy()
     {
-        Object namingDef = (_annotationIntrospector == null)? null
-                : _annotationIntrospector.findNamingStrategy(_classDef);
+        Object namingDef = _annotationIntrospector.findNamingStrategy(_classDef);
         if (namingDef == null) {
             return _config.getPropertyNamingStrategy();
         }
@@ -1071,9 +1095,11 @@
     }
 
     protected void _updateCreatorProperty(POJOPropertyBuilder prop, List<POJOPropertyBuilder> creatorProperties) {
+
         if (creatorProperties != null) {
+            final String intName = prop.getInternalName();
             for (int i = 0, len = creatorProperties.size(); i < len; ++i) {
-                if (creatorProperties.get(i).getInternalName().equals(prop.getInternalName())) {
+                if (creatorProperties.get(i).getInternalName().equals(intName)) {
                     creatorProperties.set(i, prop);
                     break;
                 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertyBuilder.java b/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertyBuilder.java
index 7cee780..dc37e28 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertyBuilder.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertyBuilder.java
@@ -4,8 +4,12 @@
 
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonSetter;
+import com.fasterxml.jackson.annotation.Nulls;
 import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.cfg.ConfigOverride;
 import com.fasterxml.jackson.databind.cfg.MapperConfig;
+import com.fasterxml.jackson.databind.type.TypeFactory;
 import com.fasterxml.jackson.databind.util.ClassUtil;
 
 /**
@@ -17,6 +21,15 @@
     implements Comparable<POJOPropertyBuilder>
 {
     /**
+     * Marker value used to denote that no reference-property information found for
+     * this property
+     *
+     * @since 2.9
+     */
+    private final static AnnotationIntrospector.ReferenceProperty NOT_REFEFERENCE_PROP =
+            AnnotationIntrospector.ReferenceProperty.managed("");
+
+    /**
      * Whether property is being composed for serialization
      * (true) or deserialization (false)
      */
@@ -47,6 +60,16 @@
 
     protected Linked<AnnotatedMethod> _setters;
 
+    protected transient PropertyMetadata _metadata;
+
+    /**
+     * Lazily accessed information about this property iff it is a forward or
+     * back reference.
+     *
+     * @since 2.9
+     */
+    protected transient AnnotationIntrospector.ReferenceProperty _referenceInfo;
+
     public POJOPropertyBuilder(MapperConfig<?> config, AnnotationIntrospector ai,
             boolean forSerialization, PropertyName internalName) {
         this(config, ai, forSerialization, internalName, internalName);
@@ -62,7 +85,8 @@
         _forSerialization = forSerialization;
     }
 
-    public POJOPropertyBuilder(POJOPropertyBuilder src, PropertyName newName)
+    // protected since 2.9 (was public before)
+    protected POJOPropertyBuilder(POJOPropertyBuilder src, PropertyName newName)
     {
         _config = src._config;
         _annotationIntrospector = src._annotationIntrospector;
@@ -74,10 +98,10 @@
         _setters = src._setters;
         _forSerialization = src._forSerialization;
     }
-    
+
     /*
     /**********************************************************
-    /* Fluent factory methods
+    /* Mutant factory methods
     /**********************************************************
      */
 
@@ -92,7 +116,7 @@
         PropertyName newName = _name.withSimpleName(newSimpleName);
         return (newName == _name) ? this : new POJOPropertyBuilder(this, newName);
     }
-    
+
     /*
     /**********************************************************
     /* Comparable implementation: sort alphabetically, except
@@ -183,7 +207,157 @@
                 || _anyExplicitNames(_ctorParameters)
                 ;
     }
-    
+
+    /*
+    /**********************************************************
+    /* Simple metadata
+    /**********************************************************
+     */
+
+    @Override
+    public PropertyMetadata getMetadata() {
+        if (_metadata == null) {
+            final Boolean b = _findRequired();
+            final String desc = _findDescription();
+            final Integer idx = _findIndex();
+            final String def = _findDefaultValue();
+            if (b == null && idx == null && def == null) {
+                _metadata = (desc == null) ? PropertyMetadata.STD_REQUIRED_OR_OPTIONAL
+                        : PropertyMetadata.STD_REQUIRED_OR_OPTIONAL.withDescription(desc);
+            } else {
+                _metadata = PropertyMetadata.construct(b, desc, idx, def);
+            }
+            if (!_forSerialization) {
+                _metadata = _getSetterInfo(_metadata);
+            }
+        }
+        return _metadata;
+    }
+
+    /**
+     * Helper method that contains logic for accessing and merging all setter
+     * information that we needed, regarding things like possible merging
+     * of property value, and handling of incoming nulls.
+     */
+    protected PropertyMetadata _getSetterInfo(PropertyMetadata metadata)
+    {
+        boolean needMerge = true;
+        Nulls valueNulls = null;
+        Nulls contentNulls = null;
+        
+        // Slightly confusing: first, annotations should be accessed via primary member
+        // (mutator); but accessor is needed for actual merge operation. So:
+        AnnotatedMember prim = getPrimaryMember();
+        AnnotatedMember acc = getAccessor();
+
+        if (prim != null) {
+            // Ok, first: does property itself have something to say?
+            if (_annotationIntrospector != null) {
+                if (acc != null) {
+                    Boolean b = _annotationIntrospector.findMergeInfo(prim);
+                    if (b != null) {
+                        needMerge = false;
+                        if (b.booleanValue()) {
+                            metadata = metadata.withMergeInfo(PropertyMetadata.MergeInfo.createForPropertyOverride(acc));
+                        }
+                    }
+                }
+                JsonSetter.Value setterInfo = _annotationIntrospector.findSetterInfo(prim);
+                if (setterInfo != null) {
+                    valueNulls = setterInfo.nonDefaultValueNulls();
+                    contentNulls = setterInfo.nonDefaultContentNulls();
+                }
+            }
+            // If not, config override?
+            // 25-Oct-2016, tatu: Either this, or type of accessor...
+            if (needMerge || (valueNulls == null) || (contentNulls == null)) {
+                Class<?> rawType = getRawPrimaryType();
+                ConfigOverride co = _config.getConfigOverride(rawType);
+                JsonSetter.Value setterInfo = co.getSetterInfo();
+                if (setterInfo != null) {
+                    if (valueNulls == null) {
+                        valueNulls = setterInfo.nonDefaultValueNulls();
+                    }
+                    if (contentNulls == null) {
+                        contentNulls = setterInfo.nonDefaultContentNulls();
+                    }
+                }
+                if (needMerge && (acc != null)) {
+                    Boolean b = co.getMergeable();
+                    if (b != null) {
+                        needMerge = false;
+                        if (b.booleanValue()) {
+                            metadata = metadata.withMergeInfo(PropertyMetadata.MergeInfo.createForTypeOverride(acc));
+                        }
+                    }
+                }
+            }
+        }
+        if (needMerge || (valueNulls == null) || (contentNulls == null)) {
+            JsonSetter.Value setterInfo = _config.getDefaultSetterInfo();
+            if (valueNulls == null) {
+                valueNulls = setterInfo.nonDefaultValueNulls();
+            }
+            if (contentNulls == null) {
+                contentNulls = setterInfo.nonDefaultContentNulls();
+            }
+            if (needMerge) {
+                Boolean b = _config.getDefaultMergeable();
+                if (Boolean.TRUE.equals(b) && (acc != null)) {
+                    metadata = metadata.withMergeInfo(PropertyMetadata.MergeInfo.createForDefaults(acc));
+                }
+            }
+        }
+        if ((valueNulls != null) || (contentNulls != null)) {
+            metadata = metadata.withNulls(valueNulls, contentNulls);
+        }
+        return metadata;
+    }
+
+    /**
+     * Type determined from the primary member for the property being built,
+     * considering precedence according to whether we are processing serialization
+     * or deserialization.
+     */
+    @Override
+    public JavaType getPrimaryType() {
+        if (_forSerialization) {
+            AnnotatedMember m = getGetter();
+            if (m == null) {
+                m = getField();
+                if (m == null) {
+                    // 09-Feb-2017, tatu: Not sure if this or `null` but...
+                    return TypeFactory.unknownType();
+                }
+                return m.getType();
+            }
+            return m.getType();
+        }
+        AnnotatedMember m = getConstructorParameter();
+        if (m == null) {
+            m = getSetter();
+            // Important: can't try direct type access for setter; what we need is
+            // type of the first parameter
+            if (m != null) {
+                return ((AnnotatedMethod) m).getParameterType(0);
+            }
+            m = getField();
+        }
+        // for setterless properties, however, can further try getter
+        if (m == null) {
+            m = getGetter();
+            if (m == null) {
+                return TypeFactory.unknownType();
+            }
+        }
+        return m.getType();
+    }
+
+    @Override
+    public Class<?> getRawPrimaryType() {
+        return getPrimaryType().getRawClass();
+    }
+
     /*
     /**********************************************************
     /* BeanPropertyDefinition implementation, accessor access
@@ -388,45 +562,18 @@
         }
         return new MemberIterator<AnnotatedParameter>(_ctorParameters);
     }
-    
-    @Override
-    public AnnotatedMember getAccessor()
-    {
-        AnnotatedMember m = getGetter();
-        if (m == null) {
-            m = getField();
-        }
-        return m;
-    }
-
-    @Override
-    public AnnotatedMember getMutator()
-    {
-        AnnotatedMember m = getConstructorParameter();
-        if (m == null) {
-            m = getSetter();
-            if (m == null) {
-                m = getField();
-            }
-        }
-        return m;
-    }
-
-    @Override
-    public AnnotatedMember getNonConstructorMutator() {
-        AnnotatedMember m = getSetter();
-        if (m == null) {
-            m = getField();
-        }
-        return m;
-    }
 
     @Override
     public AnnotatedMember getPrimaryMember() {
         if (_forSerialization) {
             return getAccessor();
         }
-        return getMutator();
+        AnnotatedMember m = getMutator();
+        // for setterless properties, however...
+        if (m == null) {
+            m = getAccessor();
+        }
+        return m;
     }
 
     protected int _getterPriority(AnnotatedMethod m)
@@ -471,12 +618,23 @@
 
     @Override
     public AnnotationIntrospector.ReferenceProperty findReferenceType() {
-        return fromMemberAnnotations(new WithMember<AnnotationIntrospector.ReferenceProperty>() {
+        // 30-Mar-2017, tatu: Access lazily but retain information since it needs
+        //   to be accessed multiple times during processing.
+        AnnotationIntrospector.ReferenceProperty result = _referenceInfo;
+        if (result != null) {
+            if (result == NOT_REFEFERENCE_PROP) {
+                return null;
+            }
+            return result;
+        }
+        result = fromMemberAnnotations(new WithMember<AnnotationIntrospector.ReferenceProperty>() {
             @Override
             public AnnotationIntrospector.ReferenceProperty withMember(AnnotatedMember member) {
                 return _annotationIntrospector.findReferenceType(member);
             }
         });
+        _referenceInfo = (result == null) ? NOT_REFEFERENCE_PROP : result;
+        return result;
     }
 
     @Override
@@ -490,19 +648,6 @@
         return (b != null) && b.booleanValue();
     }
 
-    @Override
-    public PropertyMetadata getMetadata() {
-        final Boolean b = _findRequired();
-        final String desc = _findDescription();
-        final Integer idx = _findIndex();
-        final String def = _findDefaultValue();
-        if (b == null && idx == null && def == null) {
-            return (desc == null) ? PropertyMetadata.STD_REQUIRED_OR_OPTIONAL
-                    : PropertyMetadata.STD_REQUIRED_OR_OPTIONAL.withDescription(desc);
-        }
-        return PropertyMetadata.construct(b, desc, idx, def);
-    }
-
     protected Boolean _findRequired() {
        return fromMemberAnnotations(new WithMember<Boolean>() {
             @Override
@@ -573,7 +718,7 @@
             }
         }, JsonProperty.Access.AUTO);
     }
-    
+
     /*
     /**********************************************************
     /* Data aggregation
@@ -736,7 +881,7 @@
         AnnotationMap ann = _getAllAnnotations(nodes[index]);
         while (++index < nodes.length) {
             if (nodes[index] != null) {
-              return AnnotationMap.merge(ann, _mergeAnnotations(index, nodes));
+                return AnnotationMap.merge(ann, _mergeAnnotations(index, nodes));
             }
         }
         return ann;
@@ -1073,7 +1218,7 @@
         }
         return null;
     }
-    
+
     /*
     /**********************************************************
     /* Helper classes
@@ -1140,7 +1285,7 @@
 
             if (explName) {
                 if (this.name == null) { // sanity check to catch internal problems
-                    throw new IllegalArgumentException("Can not pass true for 'explName' if name is null/empty");
+                    throw new IllegalArgumentException("Cannot pass true for 'explName' if name is null/empty");
                 }
                 // 03-Apr-2014, tatu: But how about name-space only override?
                 //   Probably should not be explicit? Or, need to merge somehow?
@@ -1228,8 +1373,8 @@
         
         @Override
         public String toString() {
-            String msg = value.toString()+"[visible="+isVisible+",ignore="+isMarkedIgnored
-                    +",explicitName="+isNameExplicit+"]";
+            String msg = String.format("%s[visible=%b,ignore=%b,explicitName=%b]",
+                    value.toString(), isVisible, isMarkedIgnored, isNameExplicit);
             if (next != null) {
                 msg = msg + ", "+next.toString();
             }
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/VirtualAnnotatedMember.java b/src/main/java/com/fasterxml/jackson/databind/introspect/VirtualAnnotatedMember.java
index 13038f0..d46f04f 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/VirtualAnnotatedMember.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/VirtualAnnotatedMember.java
@@ -3,6 +3,7 @@
 import java.lang.reflect.*;
 
 import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.util.ClassUtil;
 
 /**
  * Placeholder used by virtual properties as placeholder for
@@ -39,15 +40,6 @@
         _name = name;
     }
 
-    /**
-     * @deprecated Since 2.8
-     */
-    @Deprecated
-    public VirtualAnnotatedMember(TypeResolutionContext typeContext, Class<?> declaringClass,
-            String name, Class<?> rawType) {
-        this(typeContext, declaringClass, name, typeContext.resolveType(rawType));
-    }
-
     @Override
     public Annotated withAnnotations(AnnotationMap fallback) {
         return this;
@@ -92,12 +84,12 @@
 
     @Override
     public void setValue(Object pojo, Object value) throws IllegalArgumentException {
-        throw new IllegalArgumentException("Can not set virtual property '"+_name+"'");
+        throw new IllegalArgumentException("Cannot set virtual property '"+_name+"'");
     }
 
     @Override
     public Object getValue(Object pojo) throws IllegalArgumentException {
-        throw new IllegalArgumentException("Can not get virtual property '"+_name+"'");
+        throw new IllegalArgumentException("Cannot get virtual property '"+_name+"'");
     }
     
     /*
@@ -106,10 +98,6 @@
     /**********************************************************
      */
 
-    public String getFullName() {
-        return getDeclaringClass().getName() + "#" + getName();
-    }
-
     public int getAnnotationCount() { return 0; }
 
     @Override
@@ -120,7 +108,9 @@
     @Override
     public boolean equals(Object o) {
         if (o == this) return true;
-        if (o == null || o.getClass() != getClass()) return false;
+        if (!ClassUtil.hasClass(o, getClass())) {
+            return false;
+        }
         VirtualAnnotatedMember other = (VirtualAnnotatedMember) o;
         return (other._declaringClass == _declaringClass)
                 && other._name.equals(_name);
@@ -128,6 +118,6 @@
 
     @Override
     public String toString() {
-        return "[field "+getFullName()+"]";
+        return "[virtual "+getFullName()+"]";
     }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/VisibilityChecker.java b/src/main/java/com/fasterxml/jackson/databind/introspect/VisibilityChecker.java
index 0e2d0ad..0cb7766 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/VisibilityChecker.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/VisibilityChecker.java
@@ -29,6 +29,16 @@
     public T with(JsonAutoDetect ann);
 
     /**
+     * Method that can be used for merging default values from `this`
+     * instance with specified overrides; and either return `this`
+     * if overrides had no effect (that is, result would be equal),
+     * or a new instance with merged visibility settings.
+     *
+     * @since 2.9
+     */
+    public T withOverrides(JsonAutoDetect.Value vis);
+
+    /**
      * Builder method that will create and return an instance that has specified
      * {@link Visibility} value to use for all property elements.
      * Typical usage would be something like:
@@ -140,23 +150,7 @@
     * methods. As a result, type is declared is funky recursive generic
     * type, to allow for sub-classing of build methods with property type
     * co-variance.
-    *<p>
-    * Note on <code>JsonAutoDetect</code> annotation: it is used to
-    * access default minimum visibility access definitions.
     */
-    @JsonAutoDetect(
-        getterVisibility = Visibility.PUBLIC_ONLY,
-        isGetterVisibility = Visibility.PUBLIC_ONLY,
-        setterVisibility = Visibility.ANY,
-        /**
-         * By default, all matching single-arg constructed are found,
-         * regardless of visibility. Does not apply to factory methods,
-         * they can not be auto-detected; ditto for multiple-argument
-         * constructors.
-         */
-        creatorVisibility = Visibility.ANY,
-        fieldVisibility = Visibility.PUBLIC_ONLY
-    )
     public static class Std
         implements VisibilityChecker<Std>,
             java.io.Serializable
@@ -167,8 +161,14 @@
          * This is the canonical base instance, configured with default
          * visibility values
          */
-        protected final static Std DEFAULT = new Std(Std.class.getAnnotation(JsonAutoDetect.class));
-        
+        protected final static Std DEFAULT = new Std(
+                Visibility.PUBLIC_ONLY, // getter
+                Visibility.PUBLIC_ONLY, // is-getter
+                Visibility.ANY, // setter
+                Visibility.ANY, // creator -- legacy, to support single-arg ctors
+                Visibility.PUBLIC_ONLY // field
+                );
+
         protected final Visibility _getterMinLevel;
         protected final Visibility _isGetterMinLevel;
         protected final Visibility _setterMinLevel;
@@ -185,7 +185,7 @@
          */
         public Std(JsonAutoDetect ann)
         {
-            // let's combine checks for enabled/disabled, with minimimum level checks:
+            // let's combine checks for enabled/disabled, with minimum level checks:
             _getterMinLevel = ann.getterVisibility();
             _isGetterMinLevel = ann.isGetterVisibility();
             _setterMinLevel = ann.setterVisibility();
@@ -196,7 +196,8 @@
         /**
          * Constructor that allows directly specifying minimum visibility levels to use
          */
-        public Std(Visibility getter, Visibility isGetter, Visibility setter, Visibility creator, Visibility field)
+        public Std(Visibility getter, Visibility isGetter, Visibility setter,
+                Visibility creator, Visibility field)
         {
             _getterMinLevel = getter;
             _isGetterMinLevel = isGetter;
@@ -229,6 +230,13 @@
             }
         }
 
+        /**
+         * @since 2.9
+         */
+        public static Std construct(JsonAutoDetect.Value vis) {
+            return DEFAULT.withOverrides(vis);
+        }
+
         /*
         /********************************************************
         /* Builder/fluent methods for instantiating configured
@@ -236,20 +244,58 @@
         /********************************************************
          */
 
+        protected Std _with(Visibility g, Visibility isG, Visibility s,
+                Visibility cr, Visibility f) {
+            if ((g == _getterMinLevel)
+                    && (isG == _isGetterMinLevel)
+                    && (s == _setterMinLevel)
+                    && (cr == _creatorMinLevel)
+                    && (f == _fieldMinLevel)
+                    ) {
+                return this;
+            }
+            return new Std(g, isG, s, cr, f);
+        }
+
         @Override
         public Std with(JsonAutoDetect ann)
         {
             Std curr = this;
             if (ann != null) {
-                curr = curr.withGetterVisibility(ann.getterVisibility());
-                curr = curr.withIsGetterVisibility(ann.isGetterVisibility());
-                curr  = curr.withSetterVisibility(ann.setterVisibility());
-                curr = curr.withCreatorVisibility(ann.creatorVisibility());
-                curr = curr.withFieldVisibility(ann.fieldVisibility());
+                return _with(
+                        _defaultOrOverride(_getterMinLevel, ann.getterVisibility()),
+                        _defaultOrOverride(_isGetterMinLevel, ann.isGetterVisibility()),
+                        _defaultOrOverride(_setterMinLevel, ann.setterVisibility()),
+                        _defaultOrOverride(_creatorMinLevel, ann.creatorVisibility()),
+                        _defaultOrOverride(_fieldMinLevel, ann.fieldVisibility())
+                        );
             }
             return curr;
         }
 
+        @Override // since 2.9
+        public Std withOverrides(JsonAutoDetect.Value vis)
+        {
+            Std curr = this;
+            if (vis != null) {
+                return _with(
+                        _defaultOrOverride(_getterMinLevel, vis.getGetterVisibility()),
+                        _defaultOrOverride(_isGetterMinLevel, vis.getIsGetterVisibility()),
+                        _defaultOrOverride(_setterMinLevel, vis.getSetterVisibility()),
+                        _defaultOrOverride(_creatorMinLevel, vis.getCreatorVisibility()),
+                        _defaultOrOverride(_fieldMinLevel, vis.getFieldVisibility())
+                        );
+            }
+            return curr;
+        }
+
+        private Visibility _defaultOrOverride(Visibility defaults, Visibility override) {
+            if (override == Visibility.DEFAULT) {
+                return defaults;
+            }
+            return override;
+        }
+
         @Override
         public Std with(Visibility v)
         {
@@ -258,7 +304,7 @@
             }
             return new Std(v);
         }
-    
+
         @Override
         public Std withVisibility(PropertyAccessor method, Visibility v)
         {
@@ -284,39 +330,39 @@
 	
         @Override
         public Std withGetterVisibility(Visibility v) {
-            if (v == Visibility.DEFAULT)  v = DEFAULT._getterMinLevel;
+            if (v == Visibility.DEFAULT) v = DEFAULT._getterMinLevel;
             if (_getterMinLevel == v) return this;
             return new Std(v, _isGetterMinLevel, _setterMinLevel, _creatorMinLevel, _fieldMinLevel);
         }
 
         @Override
         public Std withIsGetterVisibility(Visibility v) {
-            if (v == Visibility.DEFAULT)  v = DEFAULT._isGetterMinLevel;
+            if (v == Visibility.DEFAULT) v = DEFAULT._isGetterMinLevel;
             if (_isGetterMinLevel == v) return this;
             return new Std(_getterMinLevel, v, _setterMinLevel, _creatorMinLevel, _fieldMinLevel);
         }
 
         @Override
         public Std withSetterVisibility(Visibility v) {
-            if (v == Visibility.DEFAULT)  v = DEFAULT._setterMinLevel;
+            if (v == Visibility.DEFAULT) v = DEFAULT._setterMinLevel;
             if (_setterMinLevel == v) return this;
             return new Std(_getterMinLevel, _isGetterMinLevel, v, _creatorMinLevel, _fieldMinLevel);
         }
-    
+
         @Override
         public Std withCreatorVisibility(Visibility v) {
-            if (v == Visibility.DEFAULT)  v = DEFAULT._creatorMinLevel;
+            if (v == Visibility.DEFAULT) v = DEFAULT._creatorMinLevel;
             if (_creatorMinLevel == v) return this;
             return new Std(_getterMinLevel, _isGetterMinLevel, _setterMinLevel, v, _fieldMinLevel);
         }
-    
+
         @Override
         public Std withFieldVisibility(Visibility v) {
             if (v == Visibility.DEFAULT)  v = DEFAULT._fieldMinLevel;
             if (_fieldMinLevel == v) return this;
             return new Std(_getterMinLevel, _isGetterMinLevel, _setterMinLevel, _creatorMinLevel, v);
         }
-		
+
         /*
         /********************************************************
         /* Public API impl
@@ -327,7 +373,7 @@
         public boolean isCreatorVisible(Member m) {
             return _creatorMinLevel.isVisible(m);
         }
-    	
+
         @Override
         public boolean isCreatorVisible(AnnotatedMember m) {
             return isCreatorVisible(m.getMember());
@@ -337,17 +383,17 @@
         public boolean isFieldVisible(Field f) {
             return _fieldMinLevel.isVisible(f);
         }
-        
+
         @Override
         public boolean isFieldVisible(AnnotatedField f) {
             return isFieldVisible(f.getAnnotated());
         }
-        
+
         @Override
         public boolean isGetterVisible(Method m) {
             return _getterMinLevel.isVisible(m);
         }
-    
+
         @Override
         public boolean isGetterVisible(AnnotatedMethod m) {
              return isGetterVisible(m.getAnnotated());
@@ -381,13 +427,8 @@
     
         @Override
         public String toString() {
-            return new StringBuilder("[Visibility:")
-            .append(" getter: ").append(_getterMinLevel)
-            .append(", isGetter: ").append(_isGetterMinLevel)
-            .append(", setter: ").append(_setterMinLevel)
-            .append(", creator: ").append(_creatorMinLevel)
-            .append(", field: ").append(_fieldMinLevel)
-            .append("]").toString();
+            return String.format("[Visibility: getter=%s,isGetter=%s,setter=%s,creator=%s,field=%s]",
+                    _getterMinLevel, _isGetterMinLevel, _setterMinLevel, _creatorMinLevel, _fieldMinLevel);
         }
     }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonObjectFormatVisitor.java b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonObjectFormatVisitor.java
index 02ba141..72dbbe1 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonObjectFormatVisitor.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonObjectFormatVisitor.java
@@ -56,8 +56,7 @@
                 JavaType propertyTypeHint) throws JsonMappingException { }
 
         @Override
-        public void optionalProperty(BeanProperty prop)
-                throws JsonMappingException { }
+        public void optionalProperty(BeanProperty prop) throws JsonMappingException { }
 
         @Override
         public void optionalProperty(String name, JsonFormatVisitable handler,
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/JsonSerializableSchema.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/JsonSerializableSchema.java
index 553206e..b34ce6d 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsonschema/JsonSerializableSchema.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsonschema/JsonSerializableSchema.java
@@ -25,7 +25,7 @@
 {
     /**
      * Marker value used to indicate that property has "no value";
-     * needed because annotations can not have null as default
+     * needed because annotations cannot have null as default
      * value.
      */
     public final static String NO_VALUE = "##irrelevant";
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/NamedType.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/NamedType.java
index e64b7f2..226668e 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/NamedType.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/NamedType.java
@@ -43,6 +43,7 @@
 
     @Override
     public String toString() {
-    	return "[NamedType, class "+_class.getName()+", name: "+(_name == null ? "null" :("'"+_name+"'"))+"]";
+    	return "[NamedType, class "+_class.getName()+", name: "
+    	        +(_name == null ? "null" :("'"+_name+"'"))+"]";
     }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/SubtypeResolver.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/SubtypeResolver.java
index e4953ca..0f6c50b 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/SubtypeResolver.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/SubtypeResolver.java
@@ -29,6 +29,11 @@
 
     public abstract void registerSubtypes(Class<?>... classes);
 
+    /**
+     * @since 2.9
+     */
+    public abstract void registerSubtypes(Collection<Class<?>> subtypes);
+    
     /*
     /**********************************************************
     /* Subtype resolution
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeDeserializer.java
index 37a86e1..b5e265f 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeDeserializer.java
@@ -66,7 +66,7 @@
     /**
      * Accessor for "default implementation" type; optionally defined
      * class to use in cases where type id is not
-     * accessible for some reason (either missing, or can not be
+     * accessible for some reason (either missing, or cannot be
      * resolved)
      */
     public abstract Class<?> getDefaultImpl();
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeResolverBuilder.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeResolverBuilder.java
index 0e630b5..7878dba 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeResolverBuilder.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeResolverBuilder.java
@@ -135,7 +135,7 @@
 
     /**
      * Method for specifying default implementation to use if type id 
-     * is either not available, or can not be resolved.
+     * is either not available, or cannot be resolved.
      * 
      * @return Resulting builder instance (usually this builder,
      *   but may be a newly constructed instance for immutable builders}
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeSerializer.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeSerializer.java
index 09f4be9..9b6e602 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeSerializer.java
@@ -3,6 +3,8 @@
 import java.io.IOException;
 
 import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.type.WritableTypeId;
+import com.fasterxml.jackson.core.util.VersionUtil;
 import com.fasterxml.jackson.databind.BeanProperty;
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
 
@@ -13,6 +15,12 @@
  * {@link com.fasterxml.jackson.databind.JsonSerializer}s using proper contextual
  * calls, to add type information using mechanism type serializer was
  * configured with.
+ *<p>
+ * NOTE: version 2.9 contains significant attempt at simplifying interface,
+ * as well as giving format implementation (via {@link JsonGenerator}) more
+ * control over actual serialization details. Minor changes are required to change
+ * call pattern so that return value of "prefix" write needs to be passed to "suffix"
+ * write.
  */
 public abstract class TypeSerializer
 {
@@ -51,107 +59,220 @@
      * property-based inclusion is used.
      */
     public abstract String getPropertyName();
-    
+
     /**
      * Accessor for object that handles conversions between
      * types and matching type ids.
      */
     public abstract TypeIdResolver getTypeIdResolver();
-    
+
     /*
     /**********************************************************
-    /* Type serialization methods
+    /* Type serialization methods: new (2.9)
     /**********************************************************
      */
-    
+
     /**
-     * Method called to write initial part of type information for given
-     * value, when it will be output as scalar JSON value (not as JSON
-     * Object or Array).
-     * This means that the context after call can not be that of JSON Object;
-     * it may be Array or root context.
-     * 
-     * @param value Value that will be serialized, for which type information is
-     *   to be written
-     * @param g Generator to use for writing type information
+     * Factory method for constructing type id value object to pass to
+     * {@link #writeTypePrefix}.
      */
-    public abstract void writeTypePrefixForScalar(Object value, JsonGenerator g) throws IOException;
+    public WritableTypeId typeId(Object value, JsonToken valueShape) {
+        WritableTypeId typeIdDef = new WritableTypeId(value, valueShape);
+        switch (getTypeInclusion()) {
+        case EXISTING_PROPERTY:
+            typeIdDef.include = WritableTypeId.Inclusion.PAYLOAD_PROPERTY;
+            typeIdDef.asProperty = getPropertyName();
+            break;
+        case EXTERNAL_PROPERTY:
+            typeIdDef.include = WritableTypeId.Inclusion.PARENT_PROPERTY;
+            typeIdDef.asProperty = getPropertyName();
+            break;
+        case PROPERTY:
+            typeIdDef.include = WritableTypeId.Inclusion.METADATA_PROPERTY;
+            typeIdDef.asProperty = getPropertyName();
+            break;
+        case WRAPPER_ARRAY:
+            typeIdDef.include = WritableTypeId.Inclusion.WRAPPER_ARRAY;
+            break;
+        case WRAPPER_OBJECT:
+            typeIdDef.include = WritableTypeId.Inclusion.WRAPPER_OBJECT;
+            break;
+        default:
+            VersionUtil.throwInternal();
+        }
+        return typeIdDef;
+    }
+
+    public WritableTypeId typeId(Object value, JsonToken valueShape,
+            Object id) {
+        WritableTypeId typeId = typeId(value, valueShape);
+        typeId.id = id;
+        return typeId;
+    }
+
+    public WritableTypeId typeId(Object value, Class<?> typeForId,
+            JsonToken valueShape) {
+        WritableTypeId typeId = typeId(value, valueShape);
+        typeId.forValueType = typeForId;
+        return typeId;
+    }
 
     /**
      * Method called to write initial part of type information for given
-     * value, when it will be output as JSON Object value (not as JSON
-     * Array or scalar).
-     * This means that context after call must be JSON Object, meaning that
-     * caller can then proceed to output field entries.
+     * value, along with possible wrapping to use: details are specified
+     * by `typeId` argument.
+     * Note that for structured types (Object, Array), this call will add
+     * necessary start token so it should NOT be explicitly written, unlike
+     * with non-type-id value writes.
+     *<p>
+     * See {@link #writeTypeSuffix(JsonGenerator, WritableTypeId)} for a complete
+     * example of typical usage.
+     *
+     * @param g Generator to use for outputting type id and possible wrapping
+     * @param typeId Details of what type id is to be written, how.
      * 
-     * @param value Value that will be serialized, for which type information is
-     *   to be written
-     * @param g Generator to use for writing type information
+     * @since 2.9
      */
-    public abstract void writeTypePrefixForObject(Object value, JsonGenerator g) throws IOException;
+    public abstract WritableTypeId writeTypePrefix(JsonGenerator g,
+            WritableTypeId typeId) throws IOException;
 
     /**
-     * Method called to write initial part of type information for given
-     * value, when it will be output as JSON Array value (not as JSON
-     * Object or scalar).
-     * This means that context after call must be JSON Array, that is, there
-     * must be an open START_ARRAY to write contents in.
-     * 
-     * @param value Value that will be serialized, for which type information is
-     *   to be written
-     * @param g Generator to use for writing type information
+     * Method that should be called after {@link #writeTypePrefix(JsonGenerator, WritableTypeId)}
+     * and matching value write have called, passing {@link WritableTypeId} returned.
+     * Usual idiom is:
+     *<pre>
+     * // Indicator generator that type identifier may be needed; generator may write
+     * // one as suggested, modify information, or take some other action 
+     * // NOTE! For Object/Array types, this will ALSO write start marker!
+     * WritableTypeId typeIdDef = typeSer.writeTypePrefix(gen,
+     *          typeSer.typeId(value, JsonToken.START_OBJECT));
+     *
+     * // serializing actual value for which TypeId may have been written... like
+     * // NOTE: do NOT write START_OBJECT before OR END_OBJECT after:
+     * g.writeStringField("message", "Hello, world!"
+     *
+     * // matching type suffix call to let generator chance to add suffix, if any
+     * // NOTE! For Object/Array types, this will ALSO write end marker!
+     * typeSer.writeTypeSuffix(gen, typeIdDef);
+     *</pre>
+     *
+     * @since 2.9
      */
-    public abstract void writeTypePrefixForArray(Object value, JsonGenerator g) throws IOException;
+    public abstract WritableTypeId writeTypeSuffix(JsonGenerator g,
+            WritableTypeId typeId) throws IOException;
+
+    /*
+    /**********************************************************
+    /* Legacy type serialization methods
+    /**********************************************************
+     */
 
     /**
-     * Method called after value has been serialized, to close any scopes opened
-     * by earlier matching call to {@link #writeTypePrefixForScalar}.
-     * Actual action to take may depend on various factors, but has to match with
-     * action {@link #writeTypePrefixForScalar} did (close array or object; or do nothing).
+     * DEPRECATED: now equivalent to:
+     *{@code writeTypePrefix(g, typeId(value, JsonToken.VALUE_STRING));}.
+     * See {@link #writeTypePrefix} for more info.
+     *
+     * @deprecated Since 2.9 use {@link #writeTypePrefix(JsonGenerator, WritableTypeId)} instead
      */
-    public abstract void writeTypeSuffixForScalar(Object value, JsonGenerator g) throws IOException;
+    @Deprecated // since 2.9
+    public void writeTypePrefixForScalar(Object value, JsonGenerator g) throws IOException {
+        writeTypePrefix(g, typeId(value, JsonToken.VALUE_STRING));
+    }
 
     /**
-     * Method called after value has been serialized, to close any scopes opened
-     * by earlier matching call to {@link #writeTypePrefixForObject}.
-     * It needs to write closing END_OBJECT marker, and any other decoration
-     * that needs to be matched.
+     * DEPRECATED: now equivalent to:
+     *{@code writeTypePrefix(g, typeId(value, JsonToken.START_OBJECT));}.
+     * See {@link #writeTypePrefix} for more info.
+     *
+     * @deprecated Since 2.9 use {@link #writeTypePrefix(JsonGenerator, WritableTypeId)} instead
      */
-    public abstract void writeTypeSuffixForObject(Object value, JsonGenerator g) throws IOException;
+    @Deprecated // since 2.9
+    public void writeTypePrefixForObject(Object value, JsonGenerator g) throws IOException {
+        writeTypePrefix(g, typeId(value, JsonToken.START_OBJECT));
+    }
 
     /**
-     * Method called after value has been serialized, to close any scopes opened
-     * by earlier matching call to {@link #writeTypeSuffixForScalar}.
-     * It needs to write closing END_ARRAY marker, and any other decoration
-     * that needs to be matched.
+     * DEPRECATED: now equivalent to:
+     *{@code writeTypePrefix(g, typeId(value, JsonToken.START_ARRAY));}.
+     * See {@link #writeTypePrefix} for more info.
+     *
+     * @deprecated Since 2.9 use {@link #writeTypePrefix(JsonGenerator, WritableTypeId)} instead
      */
-    public abstract void writeTypeSuffixForArray(Object value, JsonGenerator g) throws IOException;
+    @Deprecated // since 2.9
+    public void writeTypePrefixForArray(Object value, JsonGenerator g) throws IOException {
+        writeTypePrefix(g, typeId(value, JsonToken.START_ARRAY));
+    }
 
     /**
-     * Alternative version of the prefix-for-scalar method, which is given
-     * actual type to use (instead of using exact type of the value); typically
-     * a super type of actual value type
+     * DEPRECATED: now equivalent to:
+     *{@code writeTypeSuffix(g, typeId(value, JsonToken.VALUE_STRING));}.
+     * See {@link #writeTypeSuffix} for more info.
+     *
+     * @deprecated Since 2.9 use {@link #writeTypeSuffix(JsonGenerator, WritableTypeId)} instead
      */
+    @Deprecated // since 2.9
+    public void writeTypeSuffixForScalar(Object value, JsonGenerator g) throws IOException {
+        _writeLegacySuffix(g, typeId(value, JsonToken.VALUE_STRING));
+    }
+
+    /**
+     * DEPRECATED: now equivalent to:
+     *{@code writeTypeSuffix(g, typeId(value, JsonToken.START_OBJECT));}.
+     * See {@link #writeTypeSuffix} for more info.
+     *
+     * @deprecated Since 2.9 use {@link #writeTypeSuffix(JsonGenerator, WritableTypeId)} instead
+     */
+    @Deprecated // since 2.9
+    public void writeTypeSuffixForObject(Object value, JsonGenerator g) throws IOException {
+        _writeLegacySuffix(g, typeId(value, JsonToken.START_OBJECT));
+    }
+
+    /**
+     * DEPRECATED: now equivalent to:
+     *{@code writeTypeSuffix(g, typeId(value, JsonToken.START_ARRAY));}.
+     * See {@link #writeTypeSuffix} for more info.
+     *
+     * @deprecated Since 2.9 use {@link #writeTypeSuffix(JsonGenerator, WritableTypeId)} instead
+     */
+    @Deprecated // since 2.9
+    public void writeTypeSuffixForArray(Object value, JsonGenerator g) throws IOException {
+        _writeLegacySuffix(g, typeId(value, JsonToken.START_ARRAY));
+    }
+
+    /**
+     * DEPRECATED: now equivalent to:
+     *{@code writeTypePrefix(g, typeId(value, type, JsonToken.VALUE_STRING));}.
+     * See {@link #writeTypePrefix} for more info.
+     *
+     * @deprecated Since 2.9 use {@link #writeTypePrefix(JsonGenerator, WritableTypeId)} instead
+     */
+    @Deprecated // since 2.9
     public void writeTypePrefixForScalar(Object value, JsonGenerator g, Class<?> type) throws IOException {
-        writeTypePrefixForScalar(value, g);
+        writeTypePrefix(g, typeId(value, type, JsonToken.VALUE_STRING));
     }
 
     /**
-     * Alternative version of the prefix-for-object method, which is given
-     * actual type to use (instead of using exact type of the value); typically
-     * a super type of actual value type
+     * DEPRECATED: now equivalent to:
+     *{@code writeTypePrefix(g, typeId(value, type, JsonToken.START_OBJECT));}.
+     * See {@link #writeTypePrefix} for more info.
+     *
+     * @deprecated Since 2.9 use {@link #writeTypePrefix(JsonGenerator, WritableTypeId)} instead
      */
+    @Deprecated // since 2.9
     public void writeTypePrefixForObject(Object value, JsonGenerator g, Class<?> type) throws IOException {
-        writeTypePrefixForObject(value, g);
+        writeTypePrefix(g, typeId(value, type, JsonToken.START_OBJECT));
     }
 
     /**
-     * Alternative version of the prefix-for-array method, which is given
-     * actual type to use (instead of using exact type of the value); typically
-     * a super type of actual value type
+     * DEPRECATED: now equivalent to:
+     *{@code writeTypePrefix(g, typeId(value, type, JsonToken.START_ARRAY));}.
+     * See {@link #writeTypePrefix} for more info.
+     *
+     * @deprecated Since 2.9 use {@link #writeTypePrefix(JsonGenerator, WritableTypeId)} instead
      */
+    @Deprecated // since 2.9
     public void writeTypePrefixForArray(Object value, JsonGenerator g, Class<?> type) throws IOException {
-        writeTypePrefixForArray(value, g);
+        writeTypePrefix(g, typeId(value, type, JsonToken.START_ARRAY));
     }
 
     /*
@@ -161,40 +282,77 @@
      */
 
     /**
-     * Method called to write initial part of type information for given
-     * value, when it will be output as scalar JSON value (not as JSON
-     * Object or Array),
-     * using specified custom type id instead of calling {@link TypeIdResolver}.
-     * This means that the context after call can not be that of JSON Object;
-     * it may be Array or root context.
-     * 
-     * @param value Value that will be serialized, for which type information is
-     *   to be written
-     * @param g Generator to use for writing type information
-     * @param typeId Exact type id to use
+     * DEPRECATED: now equivalent to:
+     *{@code writeTypePrefix(g, typeId(value, JsonToken.VALUE_STRING, typeId));}.
+     * See {@link #writeTypePrefix} for more info.
+     *
+     * @deprecated Since 2.9 use {@link #writeTypePrefix(JsonGenerator, WritableTypeId)} instead
      */
-    public abstract void writeCustomTypePrefixForScalar(Object value, JsonGenerator g, String typeId) throws IOException;
+    @Deprecated // since 2.9
+    public void writeCustomTypePrefixForScalar(Object value, JsonGenerator g, String typeId) throws IOException {
+        writeTypePrefix(g, typeId(value, JsonToken.VALUE_STRING, typeId));
+    }
 
     /**
-     * Method called to write initial part of type information for given
-     * value, when it will be output as JSON Object value (not as JSON
-     * Array or scalar),
-     * using specified custom type id instead of calling {@link TypeIdResolver}.
-     * This means that context after call must be JSON Object, meaning that
-     * caller can then proceed to output field entries.
-     * 
-     * @param value Value that will be serialized, for which type information is
-     *   to be written
-     * @param g Generator to use for writing type information
-     * @param typeId Exact type id to use
+     * DEPRECATED: now equivalent to:
+     *{@code writeTypePrefix(g, typeId(value, JsonToken.START_OBJECT, typeId));}.
+     * See {@link #writeTypePrefix} for more info.
+     *
+     * @deprecated Since 2.9 use {@link #writeTypePrefix(JsonGenerator, WritableTypeId)} instead
      */
-    public abstract void writeCustomTypePrefixForObject(Object value, JsonGenerator g, String typeId) throws IOException;
-    
-    public abstract void writeCustomTypePrefixForArray(Object value, JsonGenerator g, String typeId) throws IOException;
+    @Deprecated // since 2.9
+    public void writeCustomTypePrefixForObject(Object value, JsonGenerator g, String typeId) throws IOException {
+        writeTypePrefix(g, typeId(value, JsonToken.START_OBJECT, typeId));
+    }
 
-    public abstract void writeCustomTypeSuffixForScalar(Object value, JsonGenerator g, String typeId) throws IOException;
+    /**
+     * DEPRECATED: now equivalent to:
+     *{@code writeTypePrefix(g, typeId(value, JsonToken.START_ARRAY, typeId));}.
+     * See {@link #writeTypePrefix} for more info.
+     *
+     * @deprecated Since 2.9 use {@link #writeTypePrefix(JsonGenerator, WritableTypeId)} instead
+     */
+    @Deprecated // since 2.9
+    public void writeCustomTypePrefixForArray(Object value, JsonGenerator g, String typeId) throws IOException {
+        writeTypePrefix(g, typeId(value, JsonToken.START_ARRAY, typeId));
+    }
 
-    public abstract void writeCustomTypeSuffixForObject(Object value, JsonGenerator g, String typeId) throws IOException;
+    /**
+     * @deprecated Since 2.9 use {@link #writeTypeSuffix(JsonGenerator, WritableTypeId)} instead
+     */
+    @Deprecated // since 2.9
+    public void writeCustomTypeSuffixForScalar(Object value, JsonGenerator g, String typeId) throws IOException {
+        _writeLegacySuffix(g, typeId(value, JsonToken.VALUE_STRING, typeId));
+    }
 
-    public abstract void writeCustomTypeSuffixForArray(Object value, JsonGenerator g, String typeId) throws IOException;
+    /**
+     * @deprecated Since 2.9 use {@link #writeTypeSuffix(JsonGenerator, WritableTypeId)} instead
+     */
+    @Deprecated // since 2.9
+    public void writeCustomTypeSuffixForObject(Object value, JsonGenerator g, String typeId) throws IOException {
+        _writeLegacySuffix(g, typeId(value, JsonToken.START_OBJECT, typeId));
+    }
+
+    /**
+     * @deprecated Since 2.9 use {@link #writeTypeSuffix(JsonGenerator, WritableTypeId)} instead
+     */
+    @Deprecated // since 2.9
+    public void writeCustomTypeSuffixForArray(Object value, JsonGenerator g, String typeId) throws IOException {
+        _writeLegacySuffix(g, typeId(value, JsonToken.START_ARRAY, typeId));
+    }
+
+    /**
+     * Helper method needed for backwards compatibility: since original type id
+     * can not be routed through completely, we have to reverse-engineer likely
+     * setting before calling suffix.
+     *
+     * @since 2.9
+     */
+    protected final void _writeLegacySuffix(JsonGenerator g,
+            WritableTypeId typeId) throws IOException
+    {
+        // most likely logic within generator is this:
+        typeId.wrapperWritten = !g.canWriteTypeId();
+        writeTypeSuffix(g, typeId);
+    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsArrayTypeDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsArrayTypeDeserializer.java
index 4172c17..250db2b 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsArrayTypeDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsArrayTypeDeserializer.java
@@ -116,7 +116,7 @@
         Object value = deser.deserialize(p, ctxt);
         // And then need the closing END_ARRAY
         if (hadStartArray && p.nextToken() != JsonToken.END_ARRAY) {
-            ctxt.reportWrongTokenException(p, JsonToken.END_ARRAY,
+            ctxt.reportWrongTokenException(baseType(), JsonToken.END_ARRAY,
                     "expected closing END_ARRAY after type information and deserialized value");
             // 05-May-2016, tatu: Not 100% what to do if exception is stored for
             //     future, and not thrown immediately: should probably skip until END_ARRAY
@@ -134,7 +134,7 @@
             if (_defaultImpl != null) {
                 return _idResolver.idFromBaseType();
             }
-             ctxt.reportWrongTokenException(p, JsonToken.START_ARRAY,
+             ctxt.reportWrongTokenException(baseType(), JsonToken.START_ARRAY,
                      "need JSON Array to contain As.WRAPPER_ARRAY type information for class "+baseTypeName());
              return null;
         }
@@ -148,7 +148,8 @@
         if (_defaultImpl != null) {
             return _idResolver.idFromBaseType();
         }
-        ctxt.reportWrongTokenException(p, JsonToken.VALUE_STRING, "need JSON String that contains type id (for subtype of "+baseTypeName()+")");
+        ctxt.reportWrongTokenException(baseType(), JsonToken.VALUE_STRING,
+                "need JSON String that contains type id (for subtype of %s)", baseTypeName());
         return null;
     }
 
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsArrayTypeSerializer.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsArrayTypeSerializer.java
index adde36e..1ee6f2b 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsArrayTypeSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsArrayTypeSerializer.java
@@ -1,9 +1,6 @@
 package com.fasterxml.jackson.databind.jsontype.impl;
 
-import java.io.IOException;
-
 import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
-import com.fasterxml.jackson.core.*;
 
 import com.fasterxml.jackson.databind.BeanProperty;
 import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
@@ -25,192 +22,4 @@
     
     @Override
     public As getTypeInclusion() { return As.WRAPPER_ARRAY; }
-    
-    /*
-    /**********************************************************
-    /* Writing prefixes
-    /**********************************************************
-     */
-    
-    @Override
-    public void writeTypePrefixForObject(Object value, JsonGenerator g) throws IOException {
-        final String typeId = idFromValue(value);
-        // NOTE: can not always avoid writing type id, even if null
-        if (g.canWriteTypeId()) {
-            if (typeId != null) {
-                g.writeTypeId(typeId);
-            }
-        } else {
-            g.writeStartArray();
-            g.writeString(typeId);
-        }
-        g.writeStartObject();
-    }
-
-    @Override
-    public void writeTypePrefixForObject(Object value, JsonGenerator g, Class<?> type) throws IOException {
-        final String typeId = idFromValueAndType(value, type);
-        // NOTE: can not always avoid writing type id, even if null
-        if (g.canWriteTypeId()) {
-            if (typeId != null) {
-                g.writeTypeId(typeId);
-            }
-        } else {
-            g.writeStartArray();
-            g.writeString(typeId);
-        }
-        g.writeStartObject();
-    }
-    
-    @Override
-    public void writeTypePrefixForArray(Object value, JsonGenerator g) throws IOException {
-        final String typeId = idFromValue(value);
-        if (g.canWriteTypeId()) {
-            if (typeId != null) {
-                g.writeTypeId(typeId);
-            }
-        } else {
-            g.writeStartArray();
-            g.writeString(typeId);
-        }
-        g.writeStartArray();
-    }
-
-    @Override
-    public void writeTypePrefixForArray(Object value, JsonGenerator g, Class<?> type) throws IOException {
-        final String typeId = idFromValueAndType(value, type);
-        if (g.canWriteTypeId()) {
-            if (typeId != null) {
-                g.writeTypeId(typeId);
-            }
-        } else {
-            g.writeStartArray();
-            g.writeString(typeId);
-        }
-        g.writeStartArray();
-    }
-    
-    @Override
-    public void writeTypePrefixForScalar(Object value, JsonGenerator g) throws IOException {
-        final String typeId = idFromValue(value);
-        if (g.canWriteTypeId()) {
-            if (typeId != null) {
-                g.writeTypeId(typeId);
-            }
-        } else {
-            // only need the wrapper array
-            g.writeStartArray();
-            g.writeString(typeId);
-        }
-    }
-
-    @Override
-    public void writeTypePrefixForScalar(Object value, JsonGenerator g, Class<?> type) throws IOException {
-        final String typeId = idFromValueAndType(value, type);
-        if (g.canWriteTypeId()) {
-            if (typeId != null) {
-                g.writeTypeId(typeId);
-            }
-        } else {
-            // only need the wrapper array
-            g.writeStartArray();
-            g.writeString(typeId);
-        }
-    }
-
-    /*
-    /**********************************************************
-    /* Writing suffixes
-    /**********************************************************
-     */
-    
-    @Override
-    public void writeTypeSuffixForObject(Object value, JsonGenerator g) throws IOException {
-        g.writeEndObject();
-        if (!g.canWriteTypeId()) {
-            g.writeEndArray();
-        }
-    }
-
-    @Override
-    public void writeTypeSuffixForArray(Object value, JsonGenerator g) throws IOException {
-        // first array caller needs to close, then wrapper array
-        g.writeEndArray();
-        if (!g.canWriteTypeId()) {
-            g.writeEndArray();
-        }
-    }
-
-    @Override
-    public void writeTypeSuffixForScalar(Object value, JsonGenerator g) throws IOException {
-        if (!g.canWriteTypeId()) {
-            // just the wrapper array to close
-            g.writeEndArray();
-        }
-    }
-    
-    /*
-    /**********************************************************
-    /* Writing with custom type id
-    /**********************************************************
-     */
-
-    @Override
-    public void writeCustomTypePrefixForObject(Object value, JsonGenerator g, String typeId) throws IOException {
-        if (g.canWriteTypeId()) {
-            if (typeId != null) {
-                g.writeTypeId(typeId);
-            }
-        } else {
-            g.writeStartArray();
-            g.writeString(typeId);
-        }
-        g.writeStartObject();
-    }
-    
-    @Override
-    public void writeCustomTypePrefixForArray(Object value, JsonGenerator g, String typeId) throws IOException {
-        if (g.canWriteTypeId()) {
-            if (typeId != null) {
-                g.writeTypeId(typeId);
-            }
-        } else {
-            g.writeStartArray();
-            g.writeString(typeId);
-        }
-        g.writeStartArray();
-    }
-
-    @Override
-    public void writeCustomTypePrefixForScalar(Object value, JsonGenerator g, String typeId) throws IOException {
-        if (g.canWriteTypeId()) {
-            if (typeId != null) {
-                g.writeTypeId(typeId);
-            }
-        } else {
-            g.writeStartArray();
-            g.writeString(typeId);
-        }
-    }
-
-    @Override
-    public void writeCustomTypeSuffixForObject(Object value, JsonGenerator g, String typeId) throws IOException {
-        if (!g.canWriteTypeId()) {
-            writeTypeSuffixForObject(value, g); // standard impl works fine
-        }
-    }
-
-    @Override
-    public void writeCustomTypeSuffixForArray(Object value, JsonGenerator g, String typeId) throws IOException {
-        if (!g.canWriteTypeId()) {
-            writeTypeSuffixForArray(value, g); // standard impl works fine
-        }
-    }
-
-    @Override
-    public void writeCustomTypeSuffixForScalar(Object value, JsonGenerator g, String typeId) throws IOException {
-        if (!g.canWriteTypeId()) {
-            writeTypeSuffixForScalar(value, g); // standard impl works fine
-        }
-    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsExistingPropertyTypeSerializer.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsExistingPropertyTypeSerializer.java
index fd10302..ec8ddaa 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsExistingPropertyTypeSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsExistingPropertyTypeSerializer.java
@@ -1,9 +1,6 @@
 package com.fasterxml.jackson.databind.jsontype.impl;
 
-import java.io.IOException;
-
 import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
-import com.fasterxml.jackson.core.*;
 
 import com.fasterxml.jackson.databind.BeanProperty;
 import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
@@ -15,7 +12,6 @@
 public class AsExistingPropertyTypeSerializer
     extends AsPropertyTypeSerializer
 {
-
     public AsExistingPropertyTypeSerializer(TypeIdResolver idRes,
             BeanProperty property, String propName)
     {
@@ -27,36 +23,7 @@
         return (_property == prop) ? this :
             new AsExistingPropertyTypeSerializer(_idResolver, prop, _typePropertyName);
     }
-    
-    @Override
-    public As getTypeInclusion() { return As.EXISTING_PROPERTY; }
-    
-    @Override
-    public void writeTypePrefixForObject(Object value, JsonGenerator gen) throws IOException
-    {
-        final String typeId = idFromValue(value);
-        if ((typeId != null) && gen.canWriteTypeId()) {
-            gen.writeTypeId(typeId);
-        }
-        gen.writeStartObject();
-    }
 
     @Override
-    public void writeTypePrefixForObject(Object value, JsonGenerator gen, Class<?> type) throws IOException
-    {
-        final String typeId = idFromValueAndType(value, type);
-        if ((typeId != null) && gen.canWriteTypeId()) {
-            gen.writeTypeId(typeId);
-        }
-        gen.writeStartObject();
-    }
-    
-    @Override
-    public void writeCustomTypePrefixForObject(Object value, JsonGenerator gen, String typeId) throws IOException
-    {
-        if ((typeId != null) && gen.canWriteTypeId()) {
-            gen.writeTypeId(typeId);
-        }
-        gen.writeStartObject();
-    }
+    public As getTypeInclusion() { return As.EXISTING_PROPERTY; }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsExternalTypeSerializer.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsExternalTypeSerializer.java
index 8c87391..821b6f7 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsExternalTypeSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsExternalTypeSerializer.java
@@ -11,7 +11,7 @@
  * Type serializer that preferably embeds type information as an "external"
  * type property; embedded in enclosing JSON object.
  * Note that this serializer should only be used when value is being output
- * at JSON Object context; otherwise it can not work reliably, and will have
+ * at JSON Object context; otherwise it cannot work reliably, and will have
  * to revert operation similar to {@link AsPropertyTypeSerializer}.
  *<p>
  * Note that implementation of serialization is bit cumbersome as we must
@@ -43,131 +43,38 @@
 
     /*
     /**********************************************************
-    /* Writing prefixes
+    /* Helper methods
     /**********************************************************
      */
-   
-    @Override
-    public void writeTypePrefixForObject(Object value, JsonGenerator gen) throws IOException {
-        _writeObjectPrefix(value, gen);
+
+    // nothing to wrap it with:
+    protected final void _writeScalarPrefix(Object value, JsonGenerator g) throws IOException { }
+
+    protected final void _writeObjectPrefix(Object value, JsonGenerator g) throws IOException {
+        g.writeStartObject();
     }
 
-    @Override
-    public void writeTypePrefixForObject(Object value, JsonGenerator gen, Class<?> type) throws IOException {
-        _writeObjectPrefix(value, gen);
+    protected final void _writeArrayPrefix(Object value, JsonGenerator g) throws IOException {
+        g.writeStartArray();
+    }
+   
+    protected final void _writeScalarSuffix(Object value, JsonGenerator g, String typeId) throws IOException {
+        if (typeId != null) {
+            g.writeStringField(_typePropertyName, typeId);
+        }
+    }
+   
+    protected final void _writeObjectSuffix(Object value, JsonGenerator g, String typeId) throws IOException {
+        g.writeEndObject();
+        if (typeId != null) {
+            g.writeStringField(_typePropertyName, typeId);
+        }
     }
 
-    @Override
-    public void writeTypePrefixForArray(Object value, JsonGenerator gen) throws IOException {
-        _writeArrayPrefix(value, gen);
+    protected final void _writeArraySuffix(Object value, JsonGenerator g, String typeId) throws IOException {
+        g.writeEndArray();
+        if (typeId != null) {
+            g.writeStringField(_typePropertyName, typeId);
+        }
     }
-
-    @Override
-    public void writeTypePrefixForArray(Object value, JsonGenerator gen, Class<?> type) throws IOException {
-        _writeArrayPrefix(value, gen);
-    }
-
-    @Override
-    public void writeTypePrefixForScalar(Object value, JsonGenerator gen) throws IOException {
-        _writeScalarPrefix(value, gen);
-    }
-
-    @Override
-    public void writeTypePrefixForScalar(Object value, JsonGenerator gen, Class<?> type) throws IOException {
-        _writeScalarPrefix(value, gen);
-    }
-
-    /*
-    /**********************************************************
-    /* Writing suffixes
-    /**********************************************************
-     */
-   
-   @Override
-   public void writeTypeSuffixForObject(Object value, JsonGenerator gen) throws IOException {
-       _writeObjectSuffix(value, gen, idFromValue(value));
-   }
-
-   @Override
-   public void writeTypeSuffixForArray(Object value, JsonGenerator gen) throws IOException {
-       _writeArraySuffix(value, gen, idFromValue(value));
-   }
-   
-   @Override
-   public void writeTypeSuffixForScalar(Object value, JsonGenerator gen) throws IOException {
-       _writeScalarSuffix(value, gen, idFromValue(value));
-   }
-
-   /*
-   /**********************************************************
-   /* Writing with custom type id
-   /**********************************************************
-    */
-
-   @Override
-   public void writeCustomTypePrefixForScalar(Object value, JsonGenerator gen, String typeId) throws IOException {
-       _writeScalarPrefix(value, gen);
-   }
-   
-   @Override
-   public void writeCustomTypePrefixForObject(Object value, JsonGenerator gen, String typeId) throws IOException {
-       _writeObjectPrefix(value, gen);
-   }
-   
-   @Override
-   public void writeCustomTypePrefixForArray(Object value, JsonGenerator gen, String typeId) throws IOException {
-       _writeArrayPrefix(value, gen);
-   }
-
-   @Override
-   public void writeCustomTypeSuffixForScalar(Object value, JsonGenerator gen, String typeId) throws IOException {
-       _writeScalarSuffix(value, gen, typeId);
-   }
-
-   @Override
-   public void writeCustomTypeSuffixForObject(Object value, JsonGenerator gen, String typeId) throws IOException {
-       _writeObjectSuffix(value, gen, typeId);
-   }
-
-   @Override
-   public void writeCustomTypeSuffixForArray(Object value, JsonGenerator gen, String typeId) throws IOException {
-       _writeArraySuffix(value, gen, typeId);
-   }
-
-   /*
-   /**********************************************************
-   /* Helper methods
-   /**********************************************************
-    */
-
-   // nothing to wrap it with:
-   protected final void _writeScalarPrefix(Object value, JsonGenerator gen) throws IOException { }
-
-   protected final void _writeObjectPrefix(Object value, JsonGenerator gen) throws IOException {
-       gen.writeStartObject();
-   }
-
-   protected final void _writeArrayPrefix(Object value, JsonGenerator gen) throws IOException {
-       gen.writeStartArray();
-   }
-   
-   protected final void _writeScalarSuffix(Object value, JsonGenerator gen, String typeId) throws IOException {
-       if (typeId != null) {
-           gen.writeStringField(_typePropertyName, typeId);
-       }
-   }
-   
-   protected final void _writeObjectSuffix(Object value, JsonGenerator gen, String typeId) throws IOException {
-       gen.writeEndObject();
-       if (typeId != null) {
-           gen.writeStringField(_typePropertyName, typeId);
-       }
-   }
-
-   protected final void _writeArraySuffix(Object value, JsonGenerator gen, String typeId) throws IOException {
-       gen.writeEndArray();
-       if (typeId != null) {
-           gen.writeStringField(_typePropertyName, typeId);
-       }
-   }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsPropertyTypeDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsPropertyTypeDeserializer.java
index daf3271..9bfab80 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsPropertyTypeDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsPropertyTypeDeserializer.java
@@ -106,7 +106,8 @@
     }
 
     @SuppressWarnings("resource")
-    protected Object _deserializeTypedForId(JsonParser p, DeserializationContext ctxt, TokenBuffer tb) throws IOException
+    protected Object _deserializeTypedForId(JsonParser p, DeserializationContext ctxt,
+            TokenBuffer tb) throws IOException
     {
         String typeId = p.getText();
         JsonDeserializer<Object> deser = _findDeserializer(ctxt, typeId);
@@ -131,40 +132,50 @@
     
     // off-lined to keep main method lean and mean...
     @SuppressWarnings("resource")
-    protected Object _deserializeTypedUsingDefaultImpl(JsonParser p, DeserializationContext ctxt,
-            TokenBuffer tb) throws IOException
+    protected Object _deserializeTypedUsingDefaultImpl(JsonParser p,
+            DeserializationContext ctxt, TokenBuffer tb) throws IOException
     {
-        // As per [JACKSON-614], may have default implementation to use
+        // May have default implementation to use
         JsonDeserializer<Object> deser = _findDefaultImplDeserializer(ctxt);
-        if (deser != null) {
-            if (tb != null) {
-                tb.writeEndObject();
-                p = tb.asParser(p);
-                // must move to point to the first token:
-                p.nextToken();
+        if (deser == null) {
+            // or, perhaps we just bumped into a "natural" value (boolean/int/double/String)?
+            Object result = TypeDeserializer.deserializeIfNatural(p, ctxt, _baseType);
+            if (result != null) {
+                return result;
             }
-            return deser.deserialize(p, ctxt);
-        }
-        // or, perhaps we just bumped into a "natural" value (boolean/int/double/String)?
-        Object result = TypeDeserializer.deserializeIfNatural(p, ctxt, _baseType);
-        if (result != null) {
-            return result;
-        }
-        // or, something for which "as-property" won't work, changed into "wrapper-array" type:
-        if (p.isExpectedStartArrayToken()) {
-            return super.deserializeTypedFromAny(p, ctxt);
-        }
-        if (p.hasToken(JsonToken.VALUE_STRING)) {
-            if (ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)) {
-                String str = p.getText().trim();
-                if (str.isEmpty()) {
-                    return null;
+            // or, something for which "as-property" won't work, changed into "wrapper-array" type:
+            if (p.isExpectedStartArrayToken()) {
+                return super.deserializeTypedFromAny(p, ctxt);
+            }
+            if (p.hasToken(JsonToken.VALUE_STRING)) {
+                if (ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)) {
+                    String str = p.getText().trim();
+                    if (str.isEmpty()) {
+                        return null;
+                    }
                 }
             }
+            String msg = String.format("missing type id property '%s'",
+                    _typePropertyName);
+            // even better, may know POJO property polymorphic value would be assigned to
+            if (_property != null) {
+                msg = String.format("%s (for POJO property '%s')", msg, _property.getName());
+            }
+            JavaType t = _handleMissingTypeId(ctxt, msg);
+            if (t == null) {
+                // 09-Mar-2017, tatu: Is this the right thing to do?
+                return null;
+            }
+            // ... would this actually work?
+            deser = ctxt.findContextualValueDeserializer(t, _property);
         }
-        ctxt.reportWrongTokenException(p, JsonToken.FIELD_NAME,
-                "missing property '"+_typePropertyName+"' that is to contain type id  (for class "+baseTypeName()+")");
-        return null;
+        if (tb != null) {
+            tb.writeEndObject();
+            p = tb.asParser(p);
+            // must move to point to the first token:
+            p.nextToken();
+        }
+        return deser.deserialize(p, ctxt);
     }
 
     /* Also need to re-route "unknown" version. Need to think
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsPropertyTypeSerializer.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsPropertyTypeSerializer.java
index 5da4add..3b56f91 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsPropertyTypeSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsPropertyTypeSerializer.java
@@ -1,9 +1,6 @@
 package com.fasterxml.jackson.databind.jsontype.impl;
 
-import java.io.IOException;
-
 import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
-import com.fasterxml.jackson.core.*;
 
 import com.fasterxml.jackson.databind.BeanProperty;
 import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
@@ -28,7 +25,8 @@
 
     @Override
     public AsPropertyTypeSerializer forProperty(BeanProperty prop) {
-        return (_property == prop) ? this : new AsPropertyTypeSerializer(this._idResolver, prop, this._typePropertyName);
+        return (_property == prop) ? this :
+            new AsPropertyTypeSerializer(this._idResolver, prop, this._typePropertyName);
     }
     
     @Override
@@ -36,76 +34,4 @@
 
     @Override
     public As getTypeInclusion() { return As.PROPERTY; }
-    
-    @Override
-    public void writeTypePrefixForObject(Object value, JsonGenerator jgen) throws IOException
-    {
-        final String typeId = idFromValue(value);
-        if (typeId == null) {
-            jgen.writeStartObject();
-        } else if (jgen.canWriteTypeId()) {
-            jgen.writeTypeId(typeId);
-            jgen.writeStartObject();
-        } else {
-            jgen.writeStartObject();
-            jgen.writeStringField(_typePropertyName, typeId);
-        }
-    }
-
-    @Override
-    public void writeTypePrefixForObject(Object value, JsonGenerator jgen, Class<?> type) throws IOException
-    {
-        final String typeId = idFromValueAndType(value, type);
-        if (typeId == null) {
-            jgen.writeStartObject();
-        } else if (jgen.canWriteTypeId()) {
-            jgen.writeTypeId(typeId);
-            jgen.writeStartObject();
-        } else {
-            jgen.writeStartObject();
-            jgen.writeStringField(_typePropertyName, typeId);
-        }
-    }
-    
-    //public void writeTypePrefixForArray(Object value, JsonGenerator jgen)
-    //public void writeTypePrefixForArray(Object value, JsonGenerator jgen, Class<?> type)
-    //public void writeTypePrefixForScalar(Object value, JsonGenerator jgen)
-    //public void writeTypePrefixForScalar(Object value, JsonGenerator jgen, Class<?> type)
-
-    @Override
-    public void writeTypeSuffixForObject(Object value, JsonGenerator jgen) throws IOException {
-        // always need to close, regardless of whether its native type id or not
-        jgen.writeEndObject();
-    }
-
-    //public void writeTypeSuffixForArray(Object value, JsonGenerator jgen)
-    //public void writeTypeSuffixForScalar(Object value, JsonGenerator jgen)
-
-
-    /*
-    /**********************************************************
-    /* Writing with custom type id
-    /**********************************************************
-     */
-
-    // Only need to override Object-variants
-    
-    @Override
-    public void writeCustomTypePrefixForObject(Object value, JsonGenerator jgen, String typeId) throws IOException
-    {
-        if (typeId == null) {
-            jgen.writeStartObject();
-        } else if (jgen.canWriteTypeId()) {
-            jgen.writeTypeId(typeId);
-            jgen.writeStartObject();
-        } else {
-            jgen.writeStartObject();
-            jgen.writeStringField(_typePropertyName, typeId);
-        }
-    }
-
-    @Override
-    public void writeCustomTypeSuffixForObject(Object value, JsonGenerator jgen, String typeId) throws IOException {
-        jgen.writeEndObject();
-    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeDeserializer.java
index 02f26ca..643f921 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeDeserializer.java
@@ -93,11 +93,11 @@
         if (t == JsonToken.START_OBJECT) {
             // should always get field name, but just in case...
             if (p.nextToken() != JsonToken.FIELD_NAME) {
-                ctxt.reportWrongTokenException(p, JsonToken.FIELD_NAME,
+                ctxt.reportWrongTokenException(baseType(), JsonToken.FIELD_NAME,
                         "need JSON String that contains type id (for subtype of "+baseTypeName()+")");
             }
         } else if (t != JsonToken.FIELD_NAME) {
-            ctxt.reportWrongTokenException(p, JsonToken.START_OBJECT,
+            ctxt.reportWrongTokenException(baseType(), JsonToken.START_OBJECT,
                     "need JSON Object to contain As.WRAPPER_OBJECT type information for class "+baseTypeName());
         }
         final String typeId = p.getText();
@@ -121,7 +121,7 @@
         Object value = deser.deserialize(p, ctxt);
         // And then need the closing END_OBJECT
         if (p.nextToken() != JsonToken.END_OBJECT) {
-            ctxt.reportWrongTokenException(p, JsonToken.END_OBJECT,
+            ctxt.reportWrongTokenException(baseType(), JsonToken.END_OBJECT,
                     "expected closing END_OBJECT after type information and deserialized value");
         }
         return value;
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeSerializer.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeSerializer.java
index feab592..8f12499 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeSerializer.java
@@ -7,6 +7,7 @@
 
 import com.fasterxml.jackson.databind.BeanProperty;
 import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
+import com.fasterxml.jackson.databind.util.ClassUtil;
 
 /**
  * Type wrapper that tries to use an extra JSON Object, with a single
@@ -29,203 +30,6 @@
     
     @Override
     public As getTypeInclusion() { return As.WRAPPER_OBJECT; }
-    
-    @Override
-    public void writeTypePrefixForObject(Object value, JsonGenerator g) throws IOException
-    {
-        String typeId = idFromValue(value);
-        if (g.canWriteTypeId()) {
-            if (typeId != null) {
-                g.writeTypeId(typeId);
-            }
-            g.writeStartObject();
-        } else {
-            // wrapper
-            g.writeStartObject();
-            // and then JSON Object start caller wants
-
-            // 28-Jan-2015, tatu: No really good answer here; can not really change
-            //   structure, so change null to empty String...
-            g.writeObjectFieldStart(_validTypeId(typeId));
-        }
-    }
-
-    @Override
-    public void writeTypePrefixForObject(Object value, JsonGenerator g, Class<?> type) throws IOException
-    {
-        String typeId = idFromValueAndType(value, type);
-        if (g.canWriteTypeId()) {
-            if (typeId != null) {
-                g.writeTypeId(typeId);
-            }
-            g.writeStartObject();
-        } else {
-            // wrapper
-            g.writeStartObject();
-            // and then JSON Object start caller wants
-
-            // 28-Jan-2015, tatu: No really good answer here; can not really change
-            //   structure, so change null to empty String...
-            g.writeObjectFieldStart(_validTypeId(typeId));
-        }
-    }
-    
-    @Override
-    public void writeTypePrefixForArray(Object value, JsonGenerator g) throws IOException
-    {
-        String typeId = idFromValue(value);
-        if (g.canWriteTypeId()) {
-            if (typeId != null) {
-                g.writeTypeId(typeId);
-            }
-            g.writeStartArray();
-        } else {
-            // can still wrap ok
-            g.writeStartObject();
-            g.writeArrayFieldStart(_validTypeId(typeId));
-        }
-    }
-
-    @Override
-    public void writeTypePrefixForArray(Object value, JsonGenerator g, Class<?> type) throws IOException
-    {
-        final String typeId = idFromValueAndType(value, type);
-        if (g.canWriteTypeId()) {
-            if (typeId != null) {
-                g.writeTypeId(typeId);
-            }
-            g.writeStartArray();
-        } else {
-            // can still wrap ok
-            g.writeStartObject();
-            // and then JSON Array start caller wants
-            g.writeArrayFieldStart(_validTypeId(typeId));
-        }
-    }
-
-    @Override
-    public void writeTypePrefixForScalar(Object value, JsonGenerator g) throws IOException {
-        final String typeId = idFromValue(value);
-        if (g.canWriteTypeId()) {
-            if (typeId != null) {
-                g.writeTypeId(typeId);
-            }
-        } else {
-            // can still wrap ok
-            g.writeStartObject();
-            g.writeFieldName(_validTypeId(typeId));
-        }
-    }
-
-    @Override
-    public void writeTypePrefixForScalar(Object value, JsonGenerator g, Class<?> type) throws IOException
-    {
-        final String typeId = idFromValueAndType(value, type);
-        if (g.canWriteTypeId()) {
-            if (typeId != null) {
-                g.writeTypeId(typeId);
-            }
-        } else {
-            // can still wrap ok
-            g.writeStartObject();
-            g.writeFieldName(_validTypeId(typeId));
-        }
-    }
-    
-    @Override
-    public void writeTypeSuffixForObject(Object value, JsonGenerator g) throws IOException
-    {
-        // first close JSON Object caller used
-        g.writeEndObject();
-        if (!g.canWriteTypeId()) {
-            // and then wrapper
-            g.writeEndObject();
-        }
-    }
-
-    @Override
-    public void writeTypeSuffixForArray(Object value, JsonGenerator g) throws IOException
-    {
-        // first close array caller needed
-        g.writeEndArray();
-        if (!g.canWriteTypeId()) {
-            // then wrapper object
-            g.writeEndObject();
-        }
-    }
-    
-    @Override
-    public void writeTypeSuffixForScalar(Object value, JsonGenerator g) throws IOException {
-        if (!g.canWriteTypeId()) {
-            // just need to close the wrapper object
-            g.writeEndObject();
-        }
-    }
-
-    /*
-    /**********************************************************
-    /* Writing with custom type id
-    /**********************************************************
-     */
-    
-    @Override
-    public void writeCustomTypePrefixForObject(Object value, JsonGenerator g, String typeId) throws IOException {
-        if (g.canWriteTypeId()) {
-            if (typeId != null) {
-                g.writeTypeId(typeId);
-            }
-            g.writeStartObject();
-        } else {
-            g.writeStartObject();
-            g.writeObjectFieldStart(_validTypeId(typeId));
-        }
-    }
-    
-    @Override
-    public void writeCustomTypePrefixForArray(Object value, JsonGenerator g, String typeId) throws IOException {
-        if (g.canWriteTypeId()) {
-            if (typeId != null) {
-                g.writeTypeId(typeId);
-            }
-            g.writeStartArray();
-        } else {
-            g.writeStartObject();
-            g.writeArrayFieldStart(_validTypeId(typeId));
-        }
-    }
-
-    @Override
-    public void writeCustomTypePrefixForScalar(Object value, JsonGenerator g, String typeId) throws IOException {
-        if (g.canWriteTypeId()) {
-            if (typeId != null) {
-                g.writeTypeId(typeId);
-            }
-        } else {
-            g.writeStartObject();
-            g.writeFieldName(_validTypeId(typeId));
-        }
-    }
-
-    @Override
-    public void writeCustomTypeSuffixForObject(Object value, JsonGenerator g, String typeId) throws IOException {
-        if (!g.canWriteTypeId()) {
-            writeTypeSuffixForObject(value, g); // standard impl works fine
-        }
-    }
-
-    @Override
-    public void writeCustomTypeSuffixForArray(Object value, JsonGenerator g, String typeId) throws IOException {
-        if (!g.canWriteTypeId()) {
-            writeTypeSuffixForArray(value, g); // standard impl works fine
-        }
-    }
-
-    @Override
-    public void writeCustomTypeSuffixForScalar(Object value, JsonGenerator g, String typeId) throws IOException {
-        if (!g.canWriteTypeId()) {
-            writeTypeSuffixForScalar(value, g); // standard impl works fine
-        }
-    }
 
     /*
     /**********************************************************
@@ -240,6 +44,14 @@
      * @since 2.6
      */
     protected String _validTypeId(String typeId) {
-        return (typeId == null) ? "" : typeId;
+        return ClassUtil.nonNullString(typeId);
+    }
+
+    // @since 2.9
+    protected final void _writeTypeId(JsonGenerator g, String typeId) throws IOException
+    {
+        if (typeId != null) {
+            g.writeTypeId(typeId);
+        }
     }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/ClassNameIdResolver.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/ClassNameIdResolver.java
index 2b3da1a..934cdd3 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/ClassNameIdResolver.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/ClassNameIdResolver.java
@@ -28,7 +28,7 @@
     public void registerSubtype(Class<?> type, String name) {
         // not used with class name - based resolvers
     }
-    
+
     @Override
     public String idFromValue(Object value) {
         return _idFrom(value, value.getClass(), _typeFactory);
@@ -46,43 +46,17 @@
 
     protected JavaType _typeFromId(String id, DatabindContext ctxt) throws IOException
     {
-        /* 30-Jan-2010, tatu: Most ids are basic class names; so let's first
-         *    check if any generics info is added; and only then ask factory
-         *    to do translation when necessary
-         */
-        TypeFactory tf = ctxt.getTypeFactory();
-        if (id.indexOf('<') > 0) {
-            // note: may want to try combining with specialization (esp for EnumMap)?
-            // 17-Aug-2017, tatu: As per [databind#1735] need to ensure assignment
-            //    compatibility -- needed later anyway, and not doing so may open
-            //    security issues.
-            JavaType t = tf.constructFromCanonical(id);
-            if (!t.isTypeOrSubTypeOf(_baseType.getRawClass())) {
-                // Probably cleaner to have a method in `TypeFactory` but can't add in patch
-                throw new IllegalArgumentException(String.format(
-                        "Class %s not subtype of %s", t.getRawClass().getName(), _baseType));
-            }
-            return t;
-        }
-        Class<?> cls;
-        try {
-            cls =  tf.findClass(id);
-        } catch (ClassNotFoundException e) {
-            // 24-May-2016, tatu: Ok, this is pretty ugly, but we should always get
-            //   DeserializationContext, just playing it safe
+        JavaType t = ctxt.resolveSubType(_baseType, id);
+        if (t == null) {
             if (ctxt instanceof DeserializationContext) {
-                DeserializationContext dctxt = (DeserializationContext) ctxt;
                 // First: we may have problem handlers that can deal with it?
-                return dctxt.handleUnknownTypeId(_baseType, id, this, "no such class found");
+                return ((DeserializationContext) ctxt).handleUnknownTypeId(_baseType, id, this, "no such class found");
             }
             // ... meaning that we really should never get here.
-            return null;
-        } catch (Exception e) {
-            throw new IllegalArgumentException("Invalid type id '"+id+"' (for id type 'Id.class'): "+e.getMessage(), e);
         }
-        return tf.constructSpecializedType(_baseType, cls);
+        return t;
     }
-    
+
     /*
     /**********************************************************
     /* Internal methods
@@ -114,19 +88,9 @@
                 Class<?> valueClass = Object.class;
                 // not optimal: but EnumMap is not a customizable type so this is sort of ok
                 str = typeFactory.constructMapType(EnumMap.class, enumClass, valueClass).toCanonical();
-            } else {
-                // 17-Feb-2010, tatus: Another such case: result of Arrays.asList() is
-                // named like so in Sun JDK... Let's just plain old ArrayList in its place.
-                // ... also, other similar cases exist...
-                String suffix = str.substring(JAVA_UTIL_PKG.length());
-                if (isJavaUtilCollectionClass(suffix, "List")) {
-                    str = ArrayList.class.getName();
-                } else if (isJavaUtilCollectionClass(suffix, "Map")){
-                    str = HashMap.class.getName();
-                } else if (isJavaUtilCollectionClass(suffix, "Set")){
-                    str = HashSet.class.getName();
-                }
             }
+            // 10-Jan-2018, tatu: Up until 2.9.4 we used to have other conversions for `Collections.xxx()`
+            //    and `Arrays.asList(...)`; but it was changed to be handled on receiving end instead
         } else if (str.indexOf('$') >= 0) {
             /* Other special handling may be needed for inner classes,
              * The best way to handle would be to find 'hidden' constructor; pass parent
@@ -154,17 +118,4 @@
     public String getDescForKnownTypeIds() {
         return "class name used as type id";
     }
-
-    private static boolean isJavaUtilCollectionClass(String clz, String type)
-    {
-        if (clz.startsWith("Collections$")) {
-            // 02-Jan-2017, tatu: As per [databind#1868], may need to leave Unmodifiable variants as is
-            return (clz.indexOf(type) > 0)
-                    && !clz.contains("Unmodifiable");
-        }
-        if (clz.startsWith("Arrays$")) {
-            return (clz.indexOf(type) > 0);
-        }
-        return false;
-    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/StdSubtypeResolver.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/StdSubtypeResolver.java
index 0158215..b705511 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/StdSubtypeResolver.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/StdSubtypeResolver.java
@@ -1,5 +1,6 @@
 package com.fasterxml.jackson.databind.jsontype.impl;
 
+import java.lang.reflect.Modifier;
 import java.util.*;
 
 import com.fasterxml.jackson.databind.AnnotationIntrospector;
@@ -21,7 +22,7 @@
     protected LinkedHashSet<NamedType> _registeredSubtypes;
 
     public StdSubtypeResolver() { }
-    
+
     /*
     /**********************************************************
     /* Subtype registration
@@ -47,6 +48,17 @@
         registerSubtypes(types);
     }
 
+    @Override // since 2.9
+    public void registerSubtypes(Collection<Class<?>> subtypes) {
+        int len = subtypes.size();
+        NamedType[] types = new NamedType[len];
+        int i = 0;
+        for (Class<?> subtype : subtypes) {
+            types[i++] = new NamedType(subtype);
+        }
+        registerSubtypes(types);
+    }
+
     /*
     /**********************************************************
     /* Resolution by class (serialization)
@@ -67,23 +79,27 @@
             for (NamedType subtype : _registeredSubtypes) {
                 // is it a subtype of root type?
                 if (rawBase.isAssignableFrom(subtype.getType())) { // yes
-                    AnnotatedClass curr = AnnotatedClass.constructWithoutSuperTypes(subtype.getType(), config);
+                    AnnotatedClass curr = AnnotatedClassResolver.resolveWithoutSuperTypes(config,
+                            subtype.getType());
                     _collectAndResolve(curr, subtype, config, ai, collected);
                 }
             }
         }
         
         // then annotated types for property itself
-        Collection<NamedType> st = ai.findSubtypes(property);
-        if (st != null) {
-            for (NamedType nt : st) {
-                AnnotatedClass ac = AnnotatedClass.constructWithoutSuperTypes(nt.getType(), config);
-                _collectAndResolve(ac, nt, config, ai, collected);
-            }            
+        if (property != null) {
+            Collection<NamedType> st = ai.findSubtypes(property);
+            if (st != null) {
+                for (NamedType nt : st) {
+                    AnnotatedClass ac = AnnotatedClassResolver.resolveWithoutSuperTypes(config,
+                            nt.getType());
+                    _collectAndResolve(ac, nt, config, ai, collected);
+                }            
+            }
         }
-        
+
         NamedType rootType = new NamedType(rawBase, null);
-        AnnotatedClass ac = AnnotatedClass.constructWithoutSuperTypes(rawBase, config);
+        AnnotatedClass ac = AnnotatedClassResolver.resolveWithoutSuperTypes(config, rawBase);
             
         // and finally subtypes via annotations from base type (recursively)
         _collectAndResolve(ac, rootType, config, ai, collected);
@@ -103,7 +119,8 @@
             for (NamedType subtype : _registeredSubtypes) {
                 // is it a subtype of root type?
                 if (rawBase.isAssignableFrom(subtype.getType())) { // yes
-                    AnnotatedClass curr = AnnotatedClass.constructWithoutSuperTypes(subtype.getType(), config);
+                    AnnotatedClass curr = AnnotatedClassResolver.resolveWithoutSuperTypes(config,
+                            subtype.getType());
                     _collectAndResolve(curr, subtype, config, ai, subtypes);
                 }
             }
@@ -125,7 +142,7 @@
             AnnotatedMember property, JavaType baseType)
     {
         final AnnotationIntrospector ai = config.getAnnotationIntrospector();
-        Class<?> rawBase = (baseType == null) ? property.getRawType() : baseType.getRawClass();
+        Class<?> rawBase = baseType.getRawClass();
 
         // Need to keep track of classes that have been handled already 
         Set<Class<?>> typesHandled = new HashSet<Class<?>>();
@@ -133,52 +150,56 @@
 
         // start with lowest-precedence, which is from type hierarchy
         NamedType rootType = new NamedType(rawBase, null);
-        AnnotatedClass ac = AnnotatedClass.constructWithoutSuperTypes(rawBase, config);
+        AnnotatedClass ac = AnnotatedClassResolver.resolveWithoutSuperTypes(config,
+                rawBase);
         _collectAndResolveByTypeId(ac, rootType, config, typesHandled, byName);
         
         // then with definitions from property
-        Collection<NamedType> st = ai.findSubtypes(property);
-        if (st != null) {
-            for (NamedType nt : st) {
-                ac = AnnotatedClass.constructWithoutSuperTypes(nt.getType(), config);
-                _collectAndResolveByTypeId(ac, nt, config, typesHandled, byName);
-            }            
+        if (property != null) {
+            Collection<NamedType> st = ai.findSubtypes(property);
+            if (st != null) {
+                for (NamedType nt : st) {
+                    ac = AnnotatedClassResolver.resolveWithoutSuperTypes(config, nt.getType());
+                    _collectAndResolveByTypeId(ac, nt, config, typesHandled, byName);
+                }            
+            }
         }
-        
         // and finally explicit type registrations (highest precedence)
         if (_registeredSubtypes != null) {
             for (NamedType subtype : _registeredSubtypes) {
                 // is it a subtype of root type?
                 if (rawBase.isAssignableFrom(subtype.getType())) { // yes
-                    AnnotatedClass curr = AnnotatedClass.constructWithoutSuperTypes(subtype.getType(), config);
+                    AnnotatedClass curr = AnnotatedClassResolver.resolveWithoutSuperTypes(config,
+                            subtype.getType());
                     _collectAndResolveByTypeId(curr, subtype, config, typesHandled, byName);
                 }
             }
         }
-        return _combineNamedAndUnnamed(typesHandled, byName);
+        return _combineNamedAndUnnamed(rawBase, typesHandled, byName);
     }
 
     @Override
     public Collection<NamedType> collectAndResolveSubtypesByTypeId(MapperConfig<?> config,
-            AnnotatedClass type)
+            AnnotatedClass baseType)
     {
+        final Class<?> rawBase = baseType.getRawType();
         Set<Class<?>> typesHandled = new HashSet<Class<?>>();
         Map<String,NamedType> byName = new LinkedHashMap<String,NamedType>();
 
-        NamedType rootType = new NamedType(type.getRawType(), null);
-        _collectAndResolveByTypeId(type, rootType, config, typesHandled, byName);
+        NamedType rootType = new NamedType(rawBase, null);
+        _collectAndResolveByTypeId(baseType, rootType, config, typesHandled, byName);
         
         if (_registeredSubtypes != null) {
-            Class<?> rawBase = type.getRawType();
             for (NamedType subtype : _registeredSubtypes) {
                 // is it a subtype of root type?
                 if (rawBase.isAssignableFrom(subtype.getType())) { // yes
-                    AnnotatedClass curr = AnnotatedClass.constructWithoutSuperTypes(subtype.getType(), config);
+                    AnnotatedClass curr = AnnotatedClassResolver.resolveWithoutSuperTypes(config,
+                            subtype.getType());
                     _collectAndResolveByTypeId(curr, subtype, config, typesHandled, byName);
                 }
             }
         }
-        return _combineNamedAndUnnamed(typesHandled, byName);
+        return _combineNamedAndUnnamed(rawBase, typesHandled, byName);
     }
 
     /*
@@ -218,7 +239,8 @@
         Collection<NamedType> st = ai.findSubtypes(annotatedType);
         if (st != null && !st.isEmpty()) {
             for (NamedType subtype : st) {
-                AnnotatedClass subtypeClass = AnnotatedClass.constructWithoutSuperTypes(subtype.getType(), config);
+                AnnotatedClass subtypeClass = AnnotatedClassResolver.resolveWithoutSuperTypes(config,
+                        subtype.getType());
                 _collectAndResolve(subtypeClass, subtype, config, ai, collectedSubtypes);
             }
         }
@@ -248,7 +270,8 @@
             Collection<NamedType> st = ai.findSubtypes(annotatedType);
             if (st != null && !st.isEmpty()) {
                 for (NamedType subtype : st) {
-                    AnnotatedClass subtypeClass = AnnotatedClass.constructWithoutSuperTypes(subtype.getType(), config);
+                    AnnotatedClass subtypeClass = AnnotatedClassResolver.resolveWithoutSuperTypes(config,
+                            subtype.getType());
                     _collectAndResolveByTypeId(subtypeClass, subtype, config, typesHandled, byName);
                 }
             }
@@ -259,8 +282,8 @@
      * Helper method used for merging explicitly named types and handled classes
      * without explicit names.
      */
-    protected Collection<NamedType> _combineNamedAndUnnamed(Set<Class<?>> typesHandled,
-            Map<String,NamedType> byName)
+    protected Collection<NamedType> _combineNamedAndUnnamed(Class<?> rawBase,
+            Set<Class<?>> typesHandled, Map<String,NamedType> byName)
     {
         ArrayList<NamedType> result = new ArrayList<NamedType>(byName.values());
 
@@ -271,6 +294,11 @@
             typesHandled.remove(t.getType());
         }
         for (Class<?> cls : typesHandled) {
+            // 27-Apr-2017, tatu: [databind#1616] Do not add base type itself unless
+            //     it is concrete (or has explicit type name)
+            if ((cls == rawBase) && Modifier.isAbstract(cls.getModifiers())) {
+                continue;
+            }
             result.add(new NamedType(cls));
         }
         return result;
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/StdTypeResolverBuilder.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/StdTypeResolverBuilder.java
index e5a3e4d..98e9f90 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/StdTypeResolverBuilder.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/StdTypeResolverBuilder.java
@@ -46,6 +46,16 @@
 
     public StdTypeResolverBuilder() { }
 
+    /**
+     * @since 2.9
+     */
+    protected StdTypeResolverBuilder(JsonTypeInfo.Id idType,
+            JsonTypeInfo.As idAs, String propName) {
+        _idType = idType;
+        _includeAs = idAs;
+        _typeProperty = propName;
+    }
+
     public static StdTypeResolverBuilder noTypeInfoBuilder() {
         return new StdTypeResolverBuilder().init(JsonTypeInfo.Id.NONE, null);
     }
@@ -55,7 +65,7 @@
     {
         // sanity checks
         if (idType == null) {
-            throw new IllegalArgumentException("idType can not be null");
+            throw new IllegalArgumentException("idType cannot be null");
         }
         _idType = idType;
         _customIdResolver = idRes;
@@ -110,25 +120,7 @@
 
         TypeIdResolver idRes = idResolver(config, baseType, subtypes, false, true);
 
-        JavaType defaultImpl;
-
-        if (_defaultImpl == null) {
-            defaultImpl = null;
-        } else {
-            // 20-Mar-2016, tatu: It is important to do specialization go through
-            //   TypeFactory to ensure proper resolution; with 2.7 and before, direct
-            //   call to JavaType was used, but that can not work reliably with 2.7
-            // 20-Mar-2016, tatu: Can finally add a check for type compatibility BUT
-            //   if so, need to add explicit checks for marker types. Not ideal, but
-            //   seems like a reasonable compromise.
-            if ((_defaultImpl == Void.class)
-                     || (_defaultImpl == NoClass.class)) {
-                defaultImpl = config.getTypeFactory().constructType(_defaultImpl);
-            } else {
-                defaultImpl = config.getTypeFactory()
-                    .constructSpecializedType(baseType, _defaultImpl);
-            }
-        }
+        JavaType defaultImpl = defineDefaultImpl(config, baseType);
 
         // First, method for converting type info to type id:
         switch (_includeAs) {
@@ -149,6 +141,50 @@
         throw new IllegalStateException("Do not know how to construct standard type serializer for inclusion type: "+_includeAs);
     }
 
+    protected JavaType defineDefaultImpl(DeserializationConfig config, JavaType baseType) {
+        JavaType defaultImpl;
+        if (_defaultImpl == null) {
+            //Fis of issue #955
+            if (config.isEnabled(MapperFeature.USE_BASE_TYPE_AS_DEFAULT_IMPL) && !baseType.isAbstract()) {
+                defaultImpl = baseType;
+            } else {
+                defaultImpl = null;
+            }
+        } else {
+            // 20-Mar-2016, tatu: It is important to do specialization go through
+            //   TypeFactory to ensure proper resolution; with 2.7 and before, direct
+            //   call to JavaType was used, but that cannot work reliably with 2.7
+            // 20-Mar-2016, tatu: Can finally add a check for type compatibility BUT
+            //   if so, need to add explicit checks for marker types. Not ideal, but
+            //   seems like a reasonable compromise.
+            if ((_defaultImpl == Void.class)
+                    || (_defaultImpl == NoClass.class)) {
+                defaultImpl = config.getTypeFactory().constructType(_defaultImpl);
+            } else {
+                if (baseType.hasRawClass(_defaultImpl)) { // common enough to check
+                    defaultImpl = baseType;
+                } else if (baseType.isTypeOrSuperTypeOf(_defaultImpl)) {
+                    // most common case with proper base type...
+                    defaultImpl = config.getTypeFactory()
+                            .constructSpecializedType(baseType, _defaultImpl);
+                } else {
+                    // 05-Apr-2018, tatu: As [databind#1565] and [databind#1861] need to allow
+                    //    some cases of seemingly incompatible `defaultImpl`. Easiest to just clear
+                    //    the setting.
+
+                    /*
+                    throw new IllegalArgumentException(
+                            String.format("Invalid \"defaultImpl\" (%s): not a subtype of basetype (%s)",
+                                    ClassUtil.nameOf(_defaultImpl), ClassUtil.nameOf(baseType.getRawClass()))
+                            );
+                            */
+                    defaultImpl = null;
+                }
+            }
+        }
+        return defaultImpl;
+    }
+
     /*
     /**********************************************************
     /* Construction, configuration
@@ -158,7 +194,7 @@
     @Override
     public StdTypeResolverBuilder inclusion(JsonTypeInfo.As includeAs) {
         if (includeAs == null) {
-            throw new IllegalArgumentException("includeAs can not be null");
+            throw new IllegalArgumentException("includeAs cannot be null");
         }
         _includeAs = includeAs;
         return this;
@@ -217,7 +253,7 @@
     {
         // Custom id resolver?
         if (_customIdResolver != null) { return _customIdResolver; }
-        if (_idType == null) throw new IllegalStateException("Can not build, 'init()' not yet called");
+        if (_idType == null) throw new IllegalStateException("Cannot build, 'init()' not yet called");
         switch (_idType) {
         case CLASS:
             return new ClassNameIdResolver(baseType, config.getTypeFactory());
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/SubTypeValidator.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/SubTypeValidator.java
index eb45b06..a2f7a8f 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/SubTypeValidator.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/SubTypeValidator.java
@@ -4,6 +4,7 @@
 import java.util.HashSet;
 import java.util.Set;
 
+import com.fasterxml.jackson.databind.BeanDescription;
 import com.fasterxml.jackson.databind.DeserializationContext;
 import com.fasterxml.jackson.databind.JavaType;
 import com.fasterxml.jackson.databind.JsonMappingException;
@@ -81,7 +82,8 @@
 
     public static SubTypeValidator instance() { return instance; }
 
-    public void validateSubType(DeserializationContext ctxt, JavaType type) throws JsonMappingException
+    public void validateSubType(DeserializationContext ctxt, JavaType type,
+            BeanDescription beanDesc) throws JsonMappingException
     {
         // There are certain nasty classes that could cause problems, mostly
         // via default typing -- catch them here.
@@ -123,7 +125,7 @@
             return;
         } while (false);
 
-        throw JsonMappingException.from(ctxt,
-                String.format("Illegal type (%s) to deserialize: prevented for security reasons", full));
+        ctxt.reportBadTypeDefinition(beanDesc,
+                "Illegal type (%s) to deserialize: prevented for security reasons", full);
     }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeDeserializerBase.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeDeserializerBase.java
index 7284b47..2b8e79f 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeDeserializerBase.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeDeserializerBase.java
@@ -40,7 +40,7 @@
 
     /**
      * Type to use as the default implementation, if type id is
-     * missing or can not be resolved.
+     * missing or cannot be resolved.
      */
     protected final JavaType _defaultImpl;
 
@@ -74,8 +74,7 @@
     {
         _baseType = baseType;
         _idResolver = idRes;
-        // 22-Dec-2015, tatu: as per [databind#1055], avoid NPE
-        _typePropertyName = (typePropertyName == null) ? "" : typePropertyName;
+        _typePropertyName = ClassUtil.nonNullString(typePropertyName);
         _typeIdVisible = typeIdVisible;
         // defaults are fine, although shouldn't need much concurrency
         _deserializers = new ConcurrentHashMap<String, JsonDeserializer<Object>>(16, 0.75f, 2);
@@ -117,9 +116,16 @@
 
     @Override    
     public Class<?> getDefaultImpl() {
-        return (_defaultImpl == null) ? null : _defaultImpl.getRawClass();
+        return ClassUtil.rawClass(_defaultImpl);
     }
-    
+
+    /**
+     * @since 2.9
+     */
+    public JavaType baseType() {
+        return _baseType;
+    }
+
     @Override
     public String toString()
     {
@@ -142,18 +148,18 @@
     {
         JsonDeserializer<Object> deser = _deserializers.get(typeId);
         if (deser == null) {
-            /* As per [Databind#305], need to provide contextual info. But for
+            /* As per [databind#305], need to provide contextual info. But for
              * backwards compatibility, let's start by only supporting this
              * for base class, not via interface. Later on we can add this
              * to the interface, assuming deprecation at base class helps.
              */
             JavaType type = _idResolver.typeFromId(ctxt, typeId);
             if (type == null) {
-                // As per [JACKSON-614], use the default impl if no type id available:
+                // use the default impl if no type id available:
                 deser = _findDefaultImplDeserializer(ctxt);
                 if (deser == null) {
                     // 10-May-2016, tatu: We may get some help...
-                    JavaType actual = _handleUnknownTypeId(ctxt, typeId, _idResolver, _baseType);
+                    JavaType actual = _handleUnknownTypeId(ctxt, typeId);
                     if (actual == null) { // what should this be taken to mean?
                         // TODO: try to figure out something better
                         return null;
@@ -166,7 +172,7 @@
                  *   we actually now need to explicitly narrow from base type (which may have parameterization)
                  *   using raw type.
                  *
-                 *   One complication, though; can not change 'type class' (simple type to container); otherwise
+                 *   One complication, though; cannot change 'type class' (simple type to container); otherwise
                  *   we may try to narrow a SimpleType (Object.class) into MapType (Map.class), losing actual
                  *   type in process (getting SimpleType of Map.class which will not work as expected)
                  */
@@ -246,8 +252,8 @@
              */
             deser = _findDefaultImplDeserializer(ctxt);
             if (deser == null) {
-                ctxt.reportMappingException("No (native) type id found when one was expected for polymorphic type handling");
-                return null;
+                return ctxt.reportInputMismatch(baseType(),
+                        "No (native) type id found when one was expected for polymorphic type handling");
             }
         } else {
             String typeIdStr = (typeId instanceof String) ? (String) typeId : String.valueOf(typeId);
@@ -257,7 +263,7 @@
     }
 
     /**
-     * Helper method called when given type id can not be resolved into 
+     * Helper method called when given type id cannot be resolved into 
      * concrete deserializer either directly (using given {@link  TypeIdResolver}),
      * or using default type.
      * Default implementation simply throws a {@link com.fasterxml.jackson.databind.JsonMappingException} to
@@ -269,16 +275,28 @@
      *
      * @since 2.8
      */
-    protected JavaType _handleUnknownTypeId(DeserializationContext ctxt, String typeId,
-            TypeIdResolver idResolver, JavaType baseType)
+    protected JavaType _handleUnknownTypeId(DeserializationContext ctxt, String typeId)
         throws IOException
     {
-        String extraDesc = idResolver.getDescForKnownTypeIds();
+        String extraDesc = _idResolver.getDescForKnownTypeIds();
         if (extraDesc == null) {
-            extraDesc = "known type ids are not statically known";
+            extraDesc = "type ids are not statically known";
         } else {
             extraDesc = "known type ids = " + extraDesc;
         }
-        return ctxt.handleUnknownTypeId(_baseType, typeId, idResolver, extraDesc);
+        if (_property != null) {
+            extraDesc = String.format("%s (for POJO property '%s')", extraDesc,
+                    _property.getName());
+        }
+        return ctxt.handleUnknownTypeId(_baseType, typeId, _idResolver, extraDesc);
+    }
+
+    /**
+     * @since 2.9
+     */
+    protected JavaType _handleMissingTypeId(DeserializationContext ctxt, String extraDesc)
+        throws IOException
+    {
+        return ctxt.handleMissingTypeId(_baseType, _idResolver, extraDesc);
     }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeSerializerBase.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeSerializerBase.java
index 5397946..5b79d40 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeSerializerBase.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeSerializerBase.java
@@ -1,6 +1,12 @@
 package com.fasterxml.jackson.databind.jsontype.impl;
 
+import java.io.IOException;
+
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+
+import com.fasterxml.jackson.core.type.WritableTypeId;
 import com.fasterxml.jackson.databind.BeanProperty;
 import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
 import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
@@ -17,6 +23,12 @@
         _property = property;
     }
 
+    /*
+    /**********************************************************
+    /* Base implementations, simple accessors
+    /**********************************************************
+     */
+
     @Override
     public abstract JsonTypeInfo.As getTypeInclusion();
 
@@ -26,6 +38,40 @@
     @Override
     public TypeIdResolver getTypeIdResolver() { return _idResolver; }
 
+    @Override
+    public WritableTypeId writeTypePrefix(JsonGenerator g,
+            WritableTypeId idMetadata) throws IOException
+    {
+        _generateTypeId(idMetadata);
+        return g.writeTypePrefix(idMetadata);
+    }
+
+    @Override
+    public WritableTypeId writeTypeSuffix(JsonGenerator g,
+            WritableTypeId idMetadata) throws IOException
+    {
+        return g.writeTypeSuffix(idMetadata);
+    }
+
+    /**
+     * Helper method that will generate type id to use, if not already passed.
+     *
+     * @since 2.9
+     */
+    protected void _generateTypeId(WritableTypeId idMetadata) {
+        Object id = idMetadata.id;
+        if (id == null) {
+            final Object value = idMetadata.forValue;
+            Class<?> typeForId = idMetadata.forValueType;
+            if (typeForId == null) {
+                id = idFromValue(value);
+            } else {
+                id = idFromValueAndType(value, typeForId);
+            }
+            idMetadata.id = id;
+        }
+    }
+
     /*
     /**********************************************************
     /* Helper methods for subclasses
@@ -51,8 +97,8 @@
     // As per [databind#633], maybe better just not do anything...
     protected void handleMissingId(Object value) {
         /*
-        String typeDesc = (value == null) ? "NULL" : value.getClass().getName();
-        throw new IllegalArgumentException("Can not resolve type id for "
+        String typeDesc = ClassUtil.classNameOf(value, "NULL");
+        throw new IllegalArgumentException("Cannot resolve type id for "
                 +typeDesc+" (using "+_idResolver.getClass().getName()+")");
                 */
     }
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/package-info.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/package-info.java
index fd931b0..41874d6 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/package-info.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/package-info.java
@@ -2,7 +2,7 @@
  * Package that contains interfaces that define how to implement
  * functionality for dynamically resolving type during deserialization.
  * This is needed for complete handling of polymorphic types, where
- * actual type can not be determined statically (declared type is
+ * actual type cannot be determined statically (declared type is
  * a supertype of actual polymorphic serialized types).
  */
 package com.fasterxml.jackson.databind.jsontype;
diff --git a/src/main/java/com/fasterxml/jackson/databind/module/SimpleAbstractTypeResolver.java b/src/main/java/com/fasterxml/jackson/databind/module/SimpleAbstractTypeResolver.java
index 3b0c1fd..3701950 100644
--- a/src/main/java/com/fasterxml/jackson/databind/module/SimpleAbstractTypeResolver.java
+++ b/src/main/java/com/fasterxml/jackson/databind/module/SimpleAbstractTypeResolver.java
@@ -53,14 +53,14 @@
     {
         // Sanity checks, just in case someone tries to force typing...
         if (superType == subType) {
-            throw new IllegalArgumentException("Can not add mapping from class to itself");
+            throw new IllegalArgumentException("Cannot add mapping from class to itself");
         }
         if (!superType.isAssignableFrom(subType)) {
-            throw new IllegalArgumentException("Can not add mapping from class "+superType.getName()
+            throw new IllegalArgumentException("Cannot add mapping from class "+superType.getName()
                     +" to "+subType.getName()+", as latter is not a subtype of former");
         }
         if (!Modifier.isAbstract(superType.getModifiers())) {
-            throw new IllegalArgumentException("Can not add mapping from class "+superType.getName()
+            throw new IllegalArgumentException("Cannot add mapping from class "+superType.getName()
                     +" since it is not abstract");
         }
         _mappings.put(new ClassKey(superType), subType);
diff --git a/src/main/java/com/fasterxml/jackson/databind/module/SimpleDeserializers.java b/src/main/java/com/fasterxml/jackson/databind/module/SimpleDeserializers.java
index 35c6b37..0a9f443 100644
--- a/src/main/java/com/fasterxml/jackson/databind/module/SimpleDeserializers.java
+++ b/src/main/java/com/fasterxml/jackson/databind/module/SimpleDeserializers.java
@@ -85,7 +85,7 @@
             TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
         throws JsonMappingException
     {
-        return (_classMappings == null) ? null : _classMappings.get(new ClassKey(type.getRawClass()));
+        return _find(type);
     }
 
     @Override
@@ -93,7 +93,7 @@
             DeserializationConfig config, BeanDescription beanDesc)
         throws JsonMappingException
     {
-        return (_classMappings == null) ? null : _classMappings.get(new ClassKey(type.getRawClass()));
+        return _find(type);
     }
 
     @Override
@@ -103,7 +103,7 @@
             JsonDeserializer<?> elementDeserializer)
         throws JsonMappingException
     {
-        return (_classMappings == null) ? null : _classMappings.get(new ClassKey(type.getRawClass()));
+        return _find(type);
     }
 
     @Override
@@ -113,7 +113,7 @@
             JsonDeserializer<?> elementDeserializer)
         throws JsonMappingException
     {
-        return (_classMappings == null) ? null : _classMappings.get(new ClassKey(type.getRawClass()));
+        return _find(type);
     }
     
     @Override
@@ -138,9 +138,12 @@
             DeserializationConfig config, BeanDescription beanDesc)
         throws JsonMappingException
     {
-        return (_classMappings == null) ? null : _classMappings.get(new ClassKey(nodeType));
+        if (_classMappings == null) {
+            return null;
+        }
+        return _classMappings.get(new ClassKey(nodeType));
     }
-    
+
     @Override
     public JsonDeserializer<?> findReferenceDeserializer(ReferenceType refType,
             DeserializationConfig config, BeanDescription beanDesc,
@@ -148,9 +151,9 @@
         throws JsonMappingException {
         // 21-Oct-2015, tatu: Unlikely this will really get used (reference types need more
         //    work, simple registration probably not sufficient). But whatever.
-        return (_classMappings == null) ? null : _classMappings.get(new ClassKey(refType.getRawClass()));
+        return _find(refType);
     }
-    
+
     @Override
     public JsonDeserializer<?> findMapDeserializer(MapType type,
             DeserializationConfig config, BeanDescription beanDesc,
@@ -159,7 +162,7 @@
             JsonDeserializer<?> elementDeserializer)
         throws JsonMappingException
     {
-        return (_classMappings == null) ? null : _classMappings.get(new ClassKey(type.getRawClass()));
+        return _find(type);
     }
 
     @Override
@@ -170,6 +173,13 @@
             JsonDeserializer<?> elementDeserializer)
         throws JsonMappingException
     {
-        return (_classMappings == null) ? null : _classMappings.get(new ClassKey(type.getRawClass()));
+        return _find(type);
+    }
+
+    private final JsonDeserializer<?> _find(JavaType type) {
+        if (_classMappings == null) {
+            return null;
+        }
+        return _classMappings.get(new ClassKey(type.getRawClass()));
     }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/module/SimpleModule.java b/src/main/java/com/fasterxml/jackson/databind/module/SimpleModule.java
index f6485b8..5de340e 100644
--- a/src/main/java/com/fasterxml/jackson/databind/module/SimpleModule.java
+++ b/src/main/java/com/fasterxml/jackson/databind/module/SimpleModule.java
@@ -1,5 +1,6 @@
 package com.fasterxml.jackson.databind.module;
 
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
@@ -24,16 +25,26 @@
  * override {@link #setupModule(SetupContext)} method, if they choose
  * to do so they MUST call <code>super.setupModule(context);</code>
  * to ensure that registration works as expected.
+ *<p>
+ * WARNING: when registering {@link JsonSerializer}s and {@link JsonDeserializer}s,
+ * only type erased {@code Class} is compared: this means that usually you should
+ * NOT use this implementation for registering structured types such as
+ * {@link java.util.Collection}s or {@link java.util.Map}s: this because parametric
+ * type information will not be considered and you may end up having "wrong" handler
+ * for your type.
+ * What you need to do, instead, is to implement {@link com.fasterxml.jackson.databind.deser.Deserializers} 
+ * and/or {@link com.fasterxml.jackson.databind.ser.Serializers} callbacks to match full type
+ * signatures (with {@link JavaType}).
  */
 public class SimpleModule
-    extends Module
+    extends com.fasterxml.jackson.databind.Module
     implements java.io.Serializable
 {
     private static final long serialVersionUID = 1L; // 2.5.0
 
     protected final String _name;
     protected final Version _version;
-    
+
     protected SimpleSerializers _serializers = null;
     protected SimpleDeserializers _deserializers = null;
 
@@ -251,21 +262,39 @@
     
     /*
     /**********************************************************
-    /* Configuration methods
+    /* Configuration methods, adding serializers
     /**********************************************************
      */
-    
+
+    /**
+     * Method for adding serializer to handle type that the serializer claims to handle
+     * (see {@link JsonSerializer#handledType()}).
+     *<p>
+     * WARNING! Type matching only uses type-erased {@code Class} and should NOT
+     * be used when registering serializers for generic types like
+     * {@link java.util.Collection} and {@link java.util.Map}.
+     */
     public SimpleModule addSerializer(JsonSerializer<?> ser)
     {
+        _checkNotNull(ser, "serializer");
         if (_serializers == null) {
             _serializers = new SimpleSerializers();
         }
         _serializers.addSerializer(ser);
         return this;
     }
-    
+
+    /**
+     * Method for adding serializer to handle values of specific type.
+     *<p>
+     * WARNING! Type matching only uses type-erased {@code Class} and should NOT
+     * be used when registering serializers for generic types like
+     * {@link java.util.Collection} and {@link java.util.Map}.
+     */
     public <T> SimpleModule addSerializer(Class<? extends T> type, JsonSerializer<T> ser)
     {
+        _checkNotNull(type, "type to register serializer for");
+        _checkNotNull(ser, "serializer");
         if (_serializers == null) {
             _serializers = new SimpleSerializers();
         }
@@ -275,15 +304,32 @@
 
     public <T> SimpleModule addKeySerializer(Class<? extends T> type, JsonSerializer<T> ser)
     {
+        _checkNotNull(type, "type to register key serializer for");
+        _checkNotNull(ser, "key serializer");
         if (_keySerializers == null) {
             _keySerializers = new SimpleSerializers();
         }
         _keySerializers.addSerializer(type, ser);
         return this;
     }
+
+    /*
+    /**********************************************************
+    /* Configuration methods, adding deserializers
+    /**********************************************************
+     */
     
+    /**
+     * Method for adding deserializer to handle specified type.
+     *<p>
+     * WARNING! Type matching only uses type-erased {@code Class} and should NOT
+     * be used when registering serializers for generic types like
+     * {@link java.util.Collection} and {@link java.util.Map}.
+     */
     public <T> SimpleModule addDeserializer(Class<T> type, JsonDeserializer<? extends T> deser)
     {
+        _checkNotNull(type, "type to register deserializer for");
+        _checkNotNull(deser, "deserializer");
         if (_deserializers == null) {
             _deserializers = new SimpleDeserializers();
         }
@@ -293,6 +339,8 @@
 
     public SimpleModule addKeyDeserializer(Class<?> type, KeyDeserializer deser)
     {
+        _checkNotNull(type, "type to register key deserializer for");
+        _checkNotNull(deser, "key deserializer");
         if (_keyDeserializers == null) {
             _keyDeserializers = new SimpleKeyDeserializers();
         }
@@ -300,6 +348,12 @@
         return this;
     }
 
+    /*
+    /**********************************************************
+    /* Configuration methods, type mapping
+    /**********************************************************
+     */
+
     /**
      * Lazily-constructed resolver used for storing mappings from
      * abstract classes to more specific implementing classes
@@ -308,6 +362,8 @@
     public <T> SimpleModule addAbstractTypeMapping(Class<T> superType,
             Class<? extends T> subType)
     {
+        _checkNotNull(superType, "abstract type to map");
+        _checkNotNull(subType, "concrete type to map to");
         if (_abstractTypes == null) {
             _abstractTypes = new SimpleAbstractTypeResolver();
         }
@@ -317,22 +373,6 @@
     }
 
     /**
-     * Method for registering {@link ValueInstantiator} to use when deserializing
-     * instances of type <code>beanType</code>.
-     *<p>
-     * Instantiator is
-     * registered when module is registered for <code>ObjectMapper</code>.
-     */
-    public SimpleModule addValueInstantiator(Class<?> beanType, ValueInstantiator inst)
-    {
-        if (_valueInstantiators == null) {
-            _valueInstantiators = new SimpleValueInstantiators();
-        }
-        _valueInstantiators = _valueInstantiators.addValueInstantiator(beanType, inst);
-        return this;
-    }
-
-    /**
      * Method for adding set of subtypes to be registered with
      * {@link ObjectMapper}
      * this is an alternative to using annotations in super type to indicate subtypes.
@@ -340,9 +380,10 @@
     public SimpleModule registerSubtypes(Class<?> ... subtypes)
     {
         if (_subtypes == null) {
-            _subtypes = new LinkedHashSet<NamedType>(Math.max(16, subtypes.length));
+            _subtypes = new LinkedHashSet<>();
         }
         for (Class<?> subtype : subtypes) {
+            _checkNotNull(subtype, "subtype to register");
             _subtypes.add(new NamedType(subtype));
         }
         return this;
@@ -356,15 +397,59 @@
     public SimpleModule registerSubtypes(NamedType ... subtypes)
     {
         if (_subtypes == null) {
-            _subtypes = new LinkedHashSet<NamedType>(Math.max(16, subtypes.length));
+            _subtypes = new LinkedHashSet<>();
         }
         for (NamedType subtype : subtypes) {
+            _checkNotNull(subtype, "subtype to register");
             _subtypes.add(subtype);
         }
         return this;
     }
+
+    /**
+     * Method for adding set of subtypes (along with type name to use) to be registered with
+     * {@link ObjectMapper}
+     * this is an alternative to using annotations in super type to indicate subtypes.
+     *
+     * @since 2.9
+     */
+    public SimpleModule registerSubtypes(Collection<Class<?>> subtypes)
+    {
+        if (_subtypes == null) {
+            _subtypes = new LinkedHashSet<>();
+        }
+        for (Class<?> subtype : subtypes) {
+            _checkNotNull(subtype, "subtype to register");
+            _subtypes.add(new NamedType(subtype));
+        }
+        return this;
+    }
+
+    /*
+    /**********************************************************
+    /* Configuration methods, add other handlers
+    /**********************************************************
+     */
     
     /**
+     * Method for registering {@link ValueInstantiator} to use when deserializing
+     * instances of type <code>beanType</code>.
+     *<p>
+     * Instantiator is
+     * registered when module is registered for <code>ObjectMapper</code>.
+     */
+    public SimpleModule addValueInstantiator(Class<?> beanType, ValueInstantiator inst)
+    {
+        _checkNotNull(beanType, "class to register value instantiator for");
+        _checkNotNull(inst, "value instantiator");
+        if (_valueInstantiators == null) {
+            _valueInstantiators = new SimpleValueInstantiators();
+        }
+        _valueInstantiators = _valueInstantiators.addValueInstantiator(beanType, inst);
+        return this;
+    }
+
+    /**
      * Method for specifying that annotations define by <code>mixinClass</code>
      * should be "mixed in" with annotations that <code>targetType</code>
      * has (as if they were directly included on it!).
@@ -374,13 +459,15 @@
      */
     public SimpleModule setMixInAnnotation(Class<?> targetType, Class<?> mixinClass)
     {
+        _checkNotNull(targetType, "target type");
+        _checkNotNull(mixinClass, "mixin class");
         if (_mixins == null) {
             _mixins = new HashMap<Class<?>, Class<?>>();
         }
         _mixins.put(targetType, mixinClass);
         return this;
     }
-    
+
     /*
     /**********************************************************
     /* Module impl
@@ -441,4 +528,21 @@
 
     @Override
     public Version version() { return _version; }
+
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+
+    /**
+     * @since 2.9
+     */
+    protected void _checkNotNull(Object thingy, String type)
+    {
+        if (thingy == null) {
+            throw new IllegalArgumentException(String.format(
+                    "Cannot pass `null` as %s", type));
+        }
+    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/ArrayNode.java b/src/main/java/com/fasterxml/jackson/databind/node/ArrayNode.java
index 345834a..6b49f32 100644
--- a/src/main/java/com/fasterxml/jackson/databind/node/ArrayNode.java
+++ b/src/main/java/com/fasterxml/jackson/databind/node/ArrayNode.java
@@ -1,14 +1,15 @@
 package com.fasterxml.jackson.databind.node;
 
 import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.type.WritableTypeId;
 import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.JsonSerializable;
 import com.fasterxml.jackson.databind.SerializerProvider;
 import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
 import com.fasterxml.jackson.databind.util.RawValue;
 
 import java.io.IOException;
 import java.math.BigDecimal;
+import java.math.BigInteger;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Comparator;
@@ -86,6 +87,11 @@
         return JsonNodeType.ARRAY;
     }
 
+    @Override
+    public boolean isArray() {
+        return true;
+    }
+
     @Override public JsonToken asToken() { return JsonToken.START_ARRAY; }
 
     @Override
@@ -156,24 +162,21 @@
         for (int i = 0; i < size; ++i) { // we'll typically have array list
             // For now, assuming it's either BaseJsonNode, JsonSerializable
             JsonNode n = c.get(i);
-            if (n instanceof BaseJsonNode) {
-                ((BaseJsonNode) n).serialize(f, provider);
-            } else {
-                ((JsonSerializable) n).serialize(f, provider);
-            }
+            ((BaseJsonNode) n).serialize(f, provider);
         }
         f.writeEndArray();
     }
 
     @Override
-    public void serializeWithType(JsonGenerator jg, SerializerProvider provider, TypeSerializer typeSer)
+    public void serializeWithType(JsonGenerator g, SerializerProvider provider, TypeSerializer typeSer)
         throws IOException
     {
-        typeSer.writeTypePrefixForArray(this, jg);
+        WritableTypeId typeIdDef = typeSer.writeTypePrefix(g,
+                typeSer.typeId(this, JsonToken.START_ARRAY));
         for (JsonNode n : _children) {
-            ((BaseJsonNode)n).serialize(jg, provider);
+            ((BaseJsonNode)n).serialize(g, provider);
         }
-        typeSer.writeTypeSuffixForArray(this, jg);
+        typeSer.writeTypeSuffix(g, typeIdDef);
     }
 
     /*
@@ -355,8 +358,8 @@
      */
 
     /**
-     * Method that will construct an ArrayNode and add it as a
-     * field of this ObjectNode, replacing old value, if any.
+     * Method that will construct an ArrayNode and add it at the end
+     * of this array node.
      *
      * @return Newly constructed ArrayNode
      */
@@ -521,6 +524,20 @@
     }
 
     /**
+     * Method for adding specified number at the end of this array.
+     *
+     * @return This array node, to allow chaining
+     *
+     * @since 2.9
+     */
+    public ArrayNode add(BigInteger v) {
+        if (v == null) {
+            return addNull();
+        }
+        return _add(numberNode(v));
+    }
+    
+    /**
      * Method for adding specified String value at the end of this array.
      *
      * @return This array node, to allow chaining
@@ -729,6 +746,21 @@
     }
 
     /**
+     * Method that will insert specified numeric value
+     * at specified position in this array.
+     *
+     * @return This array node, to allow chaining
+     *
+     * @since 2.9
+     */
+    public ArrayNode insert(int index, BigInteger v) {
+        if (v == null) {
+            return insertNull(index);
+        }
+        return _insert(index, numberNode(v));
+    }
+    
+    /**
      * Method that will insert specified String
      * at specified position in this array.
      *
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/BooleanNode.java b/src/main/java/com/fasterxml/jackson/databind/node/BooleanNode.java
index fa8cdee..ac3b390 100644
--- a/src/main/java/com/fasterxml/jackson/databind/node/BooleanNode.java
+++ b/src/main/java/com/fasterxml/jackson/databind/node/BooleanNode.java
@@ -20,8 +20,12 @@
     public final static BooleanNode FALSE = new BooleanNode(false);
 
     private final boolean _value;
-    
-    private BooleanNode(boolean v) { _value = v; }
+
+    /**
+     *<p>
+     * NOTE: visibility raised to `protected` in 2.9.3 to allow custom subtypes.
+     */
+    protected BooleanNode(boolean v) { _value = v; }
 
     public static BooleanNode getTrue() { return TRUE; }
     public static BooleanNode getFalse() { return FALSE; }
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/ContainerNode.java b/src/main/java/com/fasterxml/jackson/databind/node/ContainerNode.java
index f2eb608..fcfb23a 100644
--- a/src/main/java/com/fasterxml/jackson/databind/node/ContainerNode.java
+++ b/src/main/java/com/fasterxml/jackson/databind/node/ContainerNode.java
@@ -96,18 +96,16 @@
         return _nodeFactory.numberNode(v);
     }
 
-    // was missing from 2.2 and before
-    @Override
-    public final NumericNode numberNode(BigInteger v) { return _nodeFactory.numberNode(v); }
-
     @Override
     public final NumericNode numberNode(float v) { return _nodeFactory.numberNode(v); }
     @Override
     public final NumericNode numberNode(double v) { return _nodeFactory.numberNode(v); }
-    @Override
-    public final NumericNode numberNode(BigDecimal v) { return (_nodeFactory.numberNode(v)); }
 
-    // // Wrapper types, missing from 2.2 and before
+    @Override
+    public final ValueNode numberNode(BigInteger v) { return _nodeFactory.numberNode(v); }
+    @Override
+    public final ValueNode numberNode(BigDecimal v) { return (_nodeFactory.numberNode(v)); }
+
     @Override
     public final ValueNode numberNode(Byte v) { return _nodeFactory.numberNode(v); }
     @Override
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/DoubleNode.java b/src/main/java/com/fasterxml/jackson/databind/node/DoubleNode.java
index f268b85..ae14bdf 100644
--- a/src/main/java/com/fasterxml/jackson/databind/node/DoubleNode.java
+++ b/src/main/java/com/fasterxml/jackson/databind/node/DoubleNode.java
@@ -74,7 +74,7 @@
 
     @Override
     public float floatValue() { return (float) _value; }
-    
+
     @Override
     public double doubleValue() { return _value; }
 
@@ -91,11 +91,15 @@
         return NumberOutput.toString(_value);
     }
 
+    // @since 2.9
     @Override
-    public final void serialize(JsonGenerator jg, SerializerProvider provider)
-        throws IOException, JsonProcessingException
-    {
-        jg.writeNumber(_value);
+    public boolean isNaN() {
+        return Double.isNaN(_value) || Double.isInfinite(_value);
+    }
+
+    @Override
+    public final void serialize(JsonGenerator g, SerializerProvider provider) throws IOException {
+        g.writeNumber(_value);
     }
 
     @Override
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/FloatNode.java b/src/main/java/com/fasterxml/jackson/databind/node/FloatNode.java
index b4dc9fb..2d8a182 100644
--- a/src/main/java/com/fasterxml/jackson/databind/node/FloatNode.java
+++ b/src/main/java/com/fasterxml/jackson/databind/node/FloatNode.java
@@ -5,6 +5,7 @@
 import java.math.BigInteger;
 
 import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.io.NumberOutput;
 import com.fasterxml.jackson.databind.SerializerProvider;
 
 /**
@@ -74,7 +75,7 @@
 
     @Override
     public float floatValue() { return _value; }
-    
+
     @Override
     public double doubleValue() { return _value; }
 
@@ -88,16 +89,18 @@
 
     @Override
     public String asText() {
-        // As per [jackson-databind#707]
-//        return NumberOutput.toString(_value);
-        // TODO: in 2.7, call `NumberOutput.toString (added in 2.6); not yet for backwards compat
-        return Float.toString(_value);
+        return NumberOutput.toString(_value);
+    }
+
+    // @since 2.9
+    @Override
+    public boolean isNaN() {
+        return Float.isNaN(_value) || Float.isInfinite(_value);
     }
 
     @Override
-    public final void serialize(JsonGenerator jg, SerializerProvider provider) throws IOException
-    {
-        jg.writeNumber(_value);
+    public final void serialize(JsonGenerator g, SerializerProvider provider) throws IOException {
+        g.writeNumber(_value);
     }
 
     @Override
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/JsonNodeFactory.java b/src/main/java/com/fasterxml/jackson/databind/node/JsonNodeFactory.java
index 70ec2f7..e933bc8 100644
--- a/src/main/java/com/fasterxml/jackson/databind/node/JsonNodeFactory.java
+++ b/src/main/java/com/fasterxml/jackson/databind/node/JsonNodeFactory.java
@@ -183,11 +183,11 @@
      * {@link NumericNode}, but just {@link ValueNode}.
      */
     @Override
-    public ValueNode numberNode(Long value) {
-        if (value == null) {
+    public ValueNode numberNode(Long v) {
+        if (v == null) {
             return nullNode();
         }
-        return LongNode.valueOf(value.longValue());
+        return LongNode.valueOf(v.longValue());
     }
 
     /**
@@ -195,7 +195,12 @@
      * that expresses given unlimited range integer value
      */
     @Override
-    public NumericNode numberNode(BigInteger v) { return BigIntegerNode.valueOf(v); }
+    public ValueNode numberNode(BigInteger v) {
+        if (v == null) {
+            return nullNode();
+        }
+        return BigIntegerNode.valueOf(v);
+    }
 
     /**
      * Factory method for getting an instance of JSON numeric value
@@ -244,8 +249,12 @@
      * @see #JsonNodeFactory(boolean)
      */
     @Override
-    public NumericNode numberNode(BigDecimal v)
+    public ValueNode numberNode(BigDecimal v)
     {
+        if (v == null) {
+            return nullNode();
+        }
+
         /*
          * If the user wants the exact representation of this big decimal,
          * return the value directly
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/MissingNode.java b/src/main/java/com/fasterxml/jackson/databind/node/MissingNode.java
index 4e18fff..65509fe 100644
--- a/src/main/java/com/fasterxml/jackson/databind/node/MissingNode.java
+++ b/src/main/java/com/fasterxml/jackson/databind/node/MissingNode.java
@@ -24,7 +24,16 @@
 {
     private final static MissingNode instance = new MissingNode();
 
-    private MissingNode() { }
+    /**
+     *<p>
+     * NOTE: visibility raised to `protected` in 2.9.3 to allow custom subtypes.
+     */
+    protected MissingNode() { }
+
+    @Override
+    public boolean isMissingNode() {
+        return true;
+    }
 
     // Immutable: no need to copy
     @SuppressWarnings("unchecked")
@@ -61,7 +70,7 @@
         /* Nothing to output... should we signal an error tho?
          * Chances are, this is an erroneous call. For now, let's
          * not do that; serialize as explicit null. Why? Because we
-         * can not just omit a value as JSON Object field name may have
+         * cannot just omit a value as JSON Object field name may have
          * been written out.
          */
         jg.writeNull();
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/NullNode.java b/src/main/java/com/fasterxml/jackson/databind/node/NullNode.java
index 70a01ea..1a22243 100644
--- a/src/main/java/com/fasterxml/jackson/databind/node/NullNode.java
+++ b/src/main/java/com/fasterxml/jackson/databind/node/NullNode.java
@@ -17,7 +17,11 @@
 
     public final static NullNode instance = new NullNode();
 
-    private NullNode() { }
+    /**
+     *<p>
+     * NOTE: visibility raised to `protected` in 2.9.3 to allow custom subtypes.
+     */
+    protected NullNode() { }
 
     public static NullNode getInstance() { return instance; }
 
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/NumericNode.java b/src/main/java/com/fasterxml/jackson/databind/node/NumericNode.java
index 3a80971..a70a6b1 100644
--- a/src/main/java/com/fasterxml/jackson/databind/node/NumericNode.java
+++ b/src/main/java/com/fasterxml/jackson/databind/node/NumericNode.java
@@ -33,13 +33,13 @@
 
     @Override public abstract boolean canConvertToInt();
     @Override public abstract boolean canConvertToLong();
-    
+
     /* 
     /**********************************************************
     /* General type coercions
     /**********************************************************
      */
-    
+
     @Override
     public abstract String asText();
 
@@ -47,6 +47,7 @@
     public final int asInt() {
         return intValue();
     }
+
     @Override
     public final int asInt(int defaultValue) {
         return intValue();
@@ -56,17 +57,37 @@
     public final long asLong() {
         return longValue();
     }
+
     @Override
     public final long asLong(long defaultValue) {
         return longValue();
     }
-    
+
     @Override
     public final double asDouble() {
         return doubleValue();
     }
+
     @Override
     public final double asDouble(double defaultValue) {
         return doubleValue();
     }
+
+    /* 
+    /**********************************************************
+    /* Other
+    /**********************************************************
+     */
+
+    /**
+     * Convenience method for checking whether this node is a
+     * {@link FloatNode} or {@link DoubleNode} that contains
+     * "not-a-number" (NaN) value.
+     *
+     * @since 2.9
+     */
+    public boolean isNaN() {
+        return false;
+    }
+
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/ObjectNode.java b/src/main/java/com/fasterxml/jackson/databind/node/ObjectNode.java
index 387943e..c80d3ad 100644
--- a/src/main/java/com/fasterxml/jackson/databind/node/ObjectNode.java
+++ b/src/main/java/com/fasterxml/jackson/databind/node/ObjectNode.java
@@ -1,12 +1,14 @@
 package com.fasterxml.jackson.databind.node;
 
 import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.type.WritableTypeId;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
 import com.fasterxml.jackson.databind.util.RawValue;
 
 import java.io.IOException;
 import java.math.BigDecimal;
+import java.math.BigInteger;
 import java.util.*;
 
 /**
@@ -76,6 +78,11 @@
         return JsonNodeType.OBJECT;
     }
 
+    @Override
+    public final boolean isObject() {
+        return true;
+    }
+    
     @Override public JsonToken asToken() { return JsonToken.START_OBJECT; }
 
     @Override
@@ -314,7 +321,9 @@
         @SuppressWarnings("deprecation")
         boolean trimEmptyArray = (provider != null) &&
                 !provider.isEnabled(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS);
-        typeSer.writeTypePrefixForObject(this, g);
+
+        WritableTypeId typeIdDef = typeSer.writeTypePrefix(g,
+                typeSer.typeId(this, JsonToken.START_OBJECT));
         for (Map.Entry<String, JsonNode> en : _children.entrySet()) {
             BaseJsonNode value = (BaseJsonNode) en.getValue();
 
@@ -328,7 +337,7 @@
             g.writeFieldName(en.getKey());
             value.serialize(g, provider);
         }
-        typeSer.writeTypeSuffixForObject(this, g);
+        typeSer.writeTypeSuffix(g, typeIdDef);
     }
 
     /*
@@ -761,6 +770,18 @@
     }
 
     /**
+     * Method for setting value of a field to specified numeric value.
+     * 
+     * @return This node (to allow chaining)
+     *
+     * @since 2.9
+     */
+    public ObjectNode put(String fieldName, BigInteger v) {
+        return _put(fieldName, (v == null) ? nullNode()
+                : numberNode(v));
+    }
+
+    /**
      * Method for setting value of a field to specified String value.
      * 
      * @return This node (to allow chaining)
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/POJONode.java b/src/main/java/com/fasterxml/jackson/databind/node/POJONode.java
index df81fdb..06c315c 100644
--- a/src/main/java/com/fasterxml/jackson/databind/node/POJONode.java
+++ b/src/main/java/com/fasterxml/jackson/databind/node/POJONode.java
@@ -102,14 +102,16 @@
      */
 
     @Override
-    public final void serialize(JsonGenerator gen, SerializerProvider serializers) throws IOException
+    public final void serialize(JsonGenerator gen, SerializerProvider ctxt) throws IOException
     {
         if (_value == null) {
-            serializers.defaultSerializeNull(gen);
+            ctxt.defaultSerializeNull(gen);
         } else if (_value instanceof JsonSerializable) {
-            ((JsonSerializable) _value).serialize(gen, serializers);
+            ((JsonSerializable) _value).serialize(gen, ctxt);
         } else {
-            gen.writeObject(_value);
+            // 25-May-2018, tatu: [databind#1991] do not call via generator but through context;
+            //    this to preserve contextual information
+            ctxt.defaultSerializeValue(_value, gen);
         }
     }
 
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/TextNode.java b/src/main/java/com/fasterxml/jackson/databind/node/TextNode.java
index a692776..26a7f91 100644
--- a/src/main/java/com/fasterxml/jackson/databind/node/TextNode.java
+++ b/src/main/java/com/fasterxml/jackson/databind/node/TextNode.java
@@ -6,7 +6,9 @@
 import com.fasterxml.jackson.core.io.CharTypes;
 import com.fasterxml.jackson.core.io.NumberInput;
 import com.fasterxml.jackson.core.util.ByteArrayBuilder;
+
 import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.exc.InvalidFormatException;
 
 /**
  * Value node that contains a text value.
@@ -60,93 +62,16 @@
     @SuppressWarnings("resource")
     public byte[] getBinaryValue(Base64Variant b64variant) throws IOException
     {
-        ByteArrayBuilder builder = new ByteArrayBuilder(100);
-        final String str = _value;
-        int ptr = 0;
-        int len = str.length();
-
-        main_loop:
-        while (ptr < len) {
-            // first, we'll skip preceding white space, if any
-            char ch;
-            do {
-                ch = str.charAt(ptr++);
-                if (ptr >= len) {
-                    break main_loop;
-                }
-            } while (ch <= ' ');
-            int bits = b64variant.decodeBase64Char(ch);
-            if (bits < 0) {
-                _reportInvalidBase64(b64variant, ch, 0);
-            }
-            int decodedData = bits;
-            // then second base64 char; can't get padding yet, nor ws
-            if (ptr >= len) {
-                _reportBase64EOF();
-            }
-            ch = str.charAt(ptr++);
-            bits = b64variant.decodeBase64Char(ch);
-            if (bits < 0) {
-                _reportInvalidBase64(b64variant, ch, 1);
-            }
-            decodedData = (decodedData << 6) | bits;
-            // third base64 char; can be padding, but not ws
-            if (ptr >= len) {
-                // but as per [JACKSON-631] can be end-of-input, iff not using padding
-                if (!b64variant.usesPadding()) {
-                    // Got 12 bits, only need 8, need to shift
-                    decodedData >>= 4;
-                    builder.append(decodedData);
-                    break;
-                }
-                _reportBase64EOF();
-            }
-            ch = str.charAt(ptr++);
-            bits = b64variant.decodeBase64Char(ch);
-
-            // First branch: can get padding (-> 1 byte)
-            if (bits < 0) {
-                if (bits != Base64Variant.BASE64_VALUE_PADDING) {
-                    _reportInvalidBase64(b64variant, ch, 2);
-                }
-                // Ok, must get padding
-                if (ptr >= len) {
-                    _reportBase64EOF();
-                }
-                ch = str.charAt(ptr++);
-                if (!b64variant.usesPaddingChar(ch)) {
-                    _reportInvalidBase64(b64variant, ch, 3, "expected padding character '"+b64variant.getPaddingChar()+"'");
-                }
-                // Got 12 bits, only need 8, need to shift
-                decodedData >>= 4;
-                builder.append(decodedData);
-                continue;
-            }
-            // Nope, 2 or 3 bytes
-            decodedData = (decodedData << 6) | bits;
-            // fourth and last base64 char; can be padding, but not ws
-            if (ptr >= len) {
-                // but as per [JACKSON-631] can be end-of-input, iff not using padding
-                if (!b64variant.usesPadding()) {
-                    decodedData >>= 2;
-                    builder.appendTwoBytes(decodedData);
-                    break;
-                }
-                _reportBase64EOF();
-            }
-            ch = str.charAt(ptr++);
-            bits = b64variant.decodeBase64Char(ch);
-            if (bits < 0) {
-                if (bits != Base64Variant.BASE64_VALUE_PADDING) {
-                    _reportInvalidBase64(b64variant, ch, 3);
-                }
-                decodedData >>= 2;
-                builder.appendTwoBytes(decodedData);
-            } else {
-                // otherwise, our triple is now complete
-                decodedData = (decodedData << 6) | bits;
-                builder.appendThreeBytes(decodedData);
-            }
+        final String str = _value.trim();
+        ByteArrayBuilder builder = new ByteArrayBuilder(4 + ((str.length() * 3) << 2));
+        try {
+            b64variant.decode(str, builder);
+        } catch (IllegalArgumentException e) {
+            throw InvalidFormatException.from(null,
+                    String.format(
+"Cannot access contents of TextNode as binary due to broken Base64 encoding: %s",
+e.getMessage()),
+                    str, byte[].class);
         }
         return builder.toByteArray();
     }
@@ -155,7 +80,7 @@
     public byte[] binaryValue() throws IOException {
         return getBinaryValue(Base64Variants.getDefaultVariant());
     }
-    
+
     /* 
     /**********************************************************
     /* General type coercions
@@ -258,44 +183,4 @@
         CharTypes.appendQuoted(sb, content);
         sb.append('"');
     }
-
-    /*
-    /**********************************************************
-    /* Helper methods
-    /**********************************************************
-     */
-
-    protected void _reportInvalidBase64(Base64Variant b64variant, char ch, int bindex)
-        throws JsonParseException
-    {
-        _reportInvalidBase64(b64variant, ch, bindex, null);
-    }
-
-    /**
-     * @param bindex Relative index within base64 character unit; between 0
-     *   and 3 (as unit has exactly 4 characters)
-     */
-    protected void _reportInvalidBase64(Base64Variant b64variant, char ch, int bindex, String msg)
-        throws JsonParseException
-    {
-        String base;
-        if (ch <= ' ') {
-            base = "Illegal white space character (code 0x"+Integer.toHexString(ch)+") as character #"+(bindex+1)+" of 4-char base64 unit: can only used between units";
-        } else if (b64variant.usesPaddingChar(ch)) {
-            base = "Unexpected padding character ('"+b64variant.getPaddingChar()+"') as character #"+(bindex+1)+" of 4-char base64 unit: padding only legal as 3rd or 4th character";
-        } else if (!Character.isDefined(ch) || Character.isISOControl(ch)) {
-            // Not sure if we can really get here... ? (most illegal xml chars are caught at lower level)
-            base = "Illegal character (code 0x"+Integer.toHexString(ch)+") in base64 content";
-        } else {
-            base = "Illegal character '"+ch+"' (code 0x"+Integer.toHexString(ch)+") in base64 content";
-        }
-        if (msg != null) {
-            base = base + ": " + msg;
-        }
-        throw new JsonParseException(null, base, JsonLocation.NA);
-    }
-
-    protected void _reportBase64EOF() throws JsonParseException {
-        throw new JsonParseException(null, "Unexpected end-of-String when base64 content");
-    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/TreeTraversingParser.java b/src/main/java/com/fasterxml/jackson/databind/node/TreeTraversingParser.java
index 9ede6bd..aa3f309 100644
--- a/src/main/java/com/fasterxml/jackson/databind/node/TreeTraversingParser.java
+++ b/src/main/java/com/fasterxml/jackson/databind/node/TreeTraversingParser.java
@@ -334,6 +334,17 @@
         return null;
     }
 
+    @Override
+    public boolean isNaN() {
+        if (!_closed) {
+            JsonNode n = currentNode();
+            if (n instanceof NumericNode) {
+                return ((NumericNode) n).isNaN();
+            }
+        }
+        return false;
+    }
+
     /*
     /**********************************************************
     /* Public API, typed binary (base64) access
@@ -396,7 +407,7 @@
         JsonNode n = currentNode();
         if (n == null || !n.isNumber()) {
             JsonToken t = (n == null) ? null : n.asToken();
-            throw _constructError("Current token ("+t+") not numeric, can not use numeric value accessors");
+            throw _constructError("Current token ("+t+") not numeric, cannot use numeric value accessors");
         }
         return n;
     }
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/ValueNode.java b/src/main/java/com/fasterxml/jackson/databind/node/ValueNode.java
index 86eb129..363d59e 100644
--- a/src/main/java/com/fasterxml/jackson/databind/node/ValueNode.java
+++ b/src/main/java/com/fasterxml/jackson/databind/node/ValueNode.java
@@ -4,6 +4,7 @@
 import java.util.List;
 
 import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.type.WritableTypeId;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.SerializerProvider;
 import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
@@ -24,7 +25,7 @@
         // (base class checks for direct match)
         return MissingNode.getInstance();
     }
-    
+
     /**
      * All current value nodes are immutable, so we can just return
      * them as is.
@@ -36,13 +37,14 @@
     @Override public abstract JsonToken asToken();
 
     @Override
-    public void serializeWithType(JsonGenerator jg, SerializerProvider provider,
+    public void serializeWithType(JsonGenerator g, SerializerProvider provider,
             TypeSerializer typeSer)
-        throws IOException, JsonProcessingException
+        throws IOException
     {
-        typeSer.writeTypePrefixForScalar(this, jg);
-        serialize(jg, provider);
-        typeSer.writeTypeSuffixForScalar(this, jg);
+        WritableTypeId typeIdDef = typeSer.writeTypePrefix(g,
+                typeSer.typeId(this, asToken()));
+        serialize(g, provider);
+        typeSer.writeTypeSuffix(g, typeIdDef);
     }
 
     /*
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/AnyGetterWriter.java b/src/main/java/com/fasterxml/jackson/databind/ser/AnyGetterWriter.java
index bedf67e..045adea 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/AnyGetterWriter.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/AnyGetterWriter.java
@@ -38,7 +38,7 @@
     }
 
     /**
-     * @since 0.8.3
+     * @since 2.8.3
      */
     public void fixAccess(SerializationConfig config) {
         _accessor.fixAccess(
@@ -53,8 +53,9 @@
             return;
         }
         if (!(value instanceof Map<?,?>)) {
-            provider.reportMappingProblem("Value returned by 'any-getter' %s() not java.util.Map but %s",
-                    _accessor.getName(), value.getClass().getName());
+            provider.reportBadDefinition(_property.getType(), String.format(
+                    "Value returned by 'any-getter' %s() not java.util.Map but %s",
+                    _accessor.getName(), value.getClass().getName()));
         }
         // 23-Feb-2015, tatu: Nasty, but has to do (for now)
         if (_mapSerializer != null) {
@@ -69,25 +70,27 @@
      */
     public void getAndFilter(Object bean, JsonGenerator gen, SerializerProvider provider,
             PropertyFilter filter)
-                    throws Exception
+        throws Exception
     {
         Object value = _accessor.getValue(bean);
         if (value == null) {
             return;
         }
         if (!(value instanceof Map<?,?>)) {
-            provider.reportMappingProblem("Value returned by 'any-getter' (%s()) not java.util.Map but %s",
-                    _accessor.getName(), value.getClass().getName());
+            provider.reportBadDefinition(_property.getType(),
+                    String.format("Value returned by 'any-getter' (%s()) not java.util.Map but %s",
+                    _accessor.getName(), value.getClass().getName()));
         }
         // 19-Oct-2014, tatu: Should we try to support @JsonInclude options here?
         if (_mapSerializer != null) {
-            _mapSerializer.serializeFilteredFields((Map<?,?>) value, gen, provider, filter, null);
+            _mapSerializer.serializeFilteredAnyProperties(provider, gen, bean,(Map<?,?>) value,
+                    filter, null);
             return;
         }
         // ... not sure how custom handler would do it
         _serializer.serialize(value, gen, provider);
     }
-    
+
     // Note: NOT part of ResolvableSerializer...
     @SuppressWarnings("unchecked")
     public void resolve(SerializerProvider provider) throws JsonMappingException
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/BasicSerializerFactory.java b/src/main/java/com/fasterxml/jackson/databind/ser/BasicSerializerFactory.java
index 29c4a49..a3d422f 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/BasicSerializerFactory.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/BasicSerializerFactory.java
@@ -1,12 +1,12 @@
 package com.fasterxml.jackson.databind.ser;
 
-import java.lang.reflect.Method;
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.nio.ByteBuffer;
 import java.util.*;
+import java.util.concurrent.atomic.AtomicReference;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@@ -57,7 +57,7 @@
      * not instances
      */
     protected final static HashMap<String, Class<? extends JsonSerializer<?>>> _concreteLazy;
-    
+
     static {
         HashMap<String, Class<? extends JsonSerializer<?>>> concLazy
             = new HashMap<String, Class<? extends JsonSerializer<?>>>();
@@ -94,12 +94,10 @@
             Object value = en.getValue();
             if (value instanceof JsonSerializer<?>) {
                 concrete.put(en.getKey().getName(), (JsonSerializer<?>) value);
-            } else if (value instanceof Class<?>) {
+            } else {
                 @SuppressWarnings("unchecked")
                 Class<? extends JsonSerializer<?>> cls = (Class<? extends JsonSerializer<?>>) value;
                 concLazy.put(en.getKey().getName(), cls);
-            } else { // should never happen, but:
-                throw new IllegalStateException("Internal error: unrecognized value of type "+en.getClass().getName());
             }
         }
 
@@ -226,14 +224,14 @@
                 // As per [databind#47], also need to support @JsonValue
                 if (ser == null) {
                     beanDesc = config.introspect(keyType);
-                    AnnotatedMethod am = beanDesc.findJsonValueMethod();
+                    AnnotatedMember am = beanDesc.findJsonValueAccessor();
                     if (am != null) {
-                        final Class<?> rawType = am.getRawReturnType();
+                        final Class<?> rawType = am.getRawType();
                         JsonSerializer<?> delegate = StdKeySerializers.getStdKeySerializer(config,
                                 rawType, true);
-                        Method m = am.getAnnotated();
                         if (config.canOverrideAccessModifiers()) {
-                            ClassUtil.checkAndFixAccess(m, config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
+                            ClassUtil.checkAndFixAccess(am.getMember(),
+                                    config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
                         }
                         ser = new JsonValueSerializer(am, delegate);
                     } else {
@@ -310,12 +308,10 @@
         if (ser == null) {
             Class<? extends JsonSerializer<?>> serClass = _concreteLazy.get(clsName);
             if (serClass != null) {
-                try {
-                    return serClass.newInstance();
-                } catch (Exception e) {
-                    throw new IllegalStateException("Failed to instantiate standard serializer (of type "+serClass.getName()+"): "
-                            +e.getMessage(), e);
-                }
+                // 07-Jan-2017, tatu: Should never fail (since we control constructors),
+                //   but if it does will throw `IllegalArgumentException` with description,
+                //   which we could catch, re-title.
+                return ClassUtil.createInstance(serClass, false);
             }
         }
         return ser;
@@ -347,14 +343,14 @@
             return SerializableSerializer.instance;
         }
         // Second: @JsonValue for any type
-        AnnotatedMethod valueMethod = beanDesc.findJsonValueMethod();
-        if (valueMethod != null) {
-            Method m = valueMethod.getAnnotated();
+        AnnotatedMember valueAccessor = beanDesc.findJsonValueAccessor();
+        if (valueAccessor != null) {
             if (prov.canOverrideAccessModifiers()) {
-                ClassUtil.checkAndFixAccess(m, prov.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
+                ClassUtil.checkAndFixAccess(valueAccessor.getMember(),
+                        prov.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
             }
-            JsonSerializer<Object> ser = findSerializerFromAnnotation(prov, valueMethod);
-            return new JsonValueSerializer(valueMethod, ser);
+            JsonSerializer<Object> ser = findSerializerFromAnnotation(prov, valueAccessor);
+            return new JsonValueSerializer(valueAccessor, ser);
         }
         // No well-known annotations...
         return null;
@@ -393,7 +389,7 @@
             // 28-Apr-2015, tatu: TypeFactory does it all for us already so
             JavaType kt = mapEntryType.containedTypeOrUnknown(0);
             JavaType vt = mapEntryType.containedTypeOrUnknown(1);
-            return buildMapEntrySerializer(prov.getConfig(), type, beanDesc, staticTyping, kt, vt);
+            return buildMapEntrySerializer(prov, type, beanDesc, staticTyping, kt, vt);
         }
         if (ByteBuffer.class.isAssignableFrom(raw)) {
             return new ByteBufferSerializer();
@@ -544,7 +540,7 @@
          *   leave it as is; no clean way to make it work.
          */
         if (!staticTyping && type.useStaticType()) {
-            if (!type.isContainerType() || type.getContentType().getRawClass() != Object.class) {
+            if (!type.isContainerType() || !type.getContentType().isJavaLangObject()) {
                 staticTyping = true;
             }
         }
@@ -554,7 +550,7 @@
         TypeSerializer elementTypeSerializer = createTypeSerializer(config,
                 elementType);
 
-        // if elements have type serializer, can not force static typing:
+        // if elements have type serializer, cannot force static typing:
         if (elementTypeSerializer != null) {
             staticTyping = false;
         }
@@ -660,7 +656,7 @@
                 // We may also want to use serialize Collections "as beans", if (and only if)
                 // this is specified with `@JsonFormat(shape=Object)`
                 JsonFormat.Value format = beanDesc.findExpectedFormat(null);
-                if (format != null && format.getShape() == JsonFormat.Shape.OBJECT) {
+                if ((format != null) && format.getShape() == JsonFormat.Shape.OBJECT) {
                     return null;
                 }
                 Class<?> raw = type.getRawClass();
@@ -677,7 +673,7 @@
                     if (isIndexedList(raw)) {
                         if (elementRaw == String.class) {
                             // [JACKSON-829] Must NOT use if we have custom serializer
-                            if (elementValueSerializer == null || ClassUtil.isJacksonStdImpl(elementValueSerializer)) {
+                            if (ClassUtil.isJacksonStdImpl(elementValueSerializer)) {
                                 ser = IndexedStringListSerializer.instance;
                             }
                         } else {
@@ -686,7 +682,7 @@
                         }
                     } else if (elementRaw == String.class) {
                         // [JACKSON-829] Must NOT use if we have custom serializer
-                        if (elementValueSerializer == null || ClassUtil.isJacksonStdImpl(elementValueSerializer)) {
+                        if (ClassUtil.isJacksonStdImpl(elementValueSerializer)) {
                             ser = StringCollectionSerializer.instance;
                         }
                     }
@@ -721,6 +717,7 @@
             boolean staticTyping, TypeSerializer vts, JsonSerializer<Object> valueSerializer) {
         return new IndexedListSerializer(elemType, staticTyping, vts, valueSerializer);
     }
+
     public ContainerSerializer<?> buildCollectionSerializer(JavaType elemType,
             boolean staticTyping, TypeSerializer vts, JsonSerializer<Object> valueSerializer) {
         return new CollectionSerializer(elemType, staticTyping, vts, valueSerializer);
@@ -735,7 +732,7 @@
     /* Factory methods, for Maps
     /**********************************************************
      */
-    
+
     /**
      * Helper method that handles configuration details when constructing serializers for
      * {@link java.util.Map} types.
@@ -746,7 +743,13 @@
             TypeSerializer elementTypeSerializer, JsonSerializer<Object> elementValueSerializer)
         throws JsonMappingException
     {
-        final SerializationConfig config = prov.getConfig();
+        // [databind#467]: This is where we could allow serialization "as POJO": But! It's
+        // nasty to undo, and does not apply on per-property basis. So, hardly optimal
+        JsonFormat.Value format = beanDesc.findExpectedFormat(null);
+        if ((format != null) && format.getShape() == JsonFormat.Shape.OBJECT) {
+            return null;
+        }
+
         JsonSerializer<?> ser = null;
 
         // Order of lookups:
@@ -754,6 +757,7 @@
         // 2. Annotations (@JsonValue, @JsonDeserialize)
         // 3. Defaults
         
+        final SerializationConfig config = prov.getConfig();
         for (Serializers serializers : customSerializers()) { // (1) Custom
             ser = serializers.findMapSerializer(config, type, beanDesc,
                     keySerializer, elementTypeSerializer, elementValueSerializer);
@@ -774,12 +778,7 @@
                 MapSerializer mapSer = MapSerializer.construct(ignored,
                         type, staticTyping, elementTypeSerializer,
                         keySerializer, elementValueSerializer, filterId);
-                Object suppressableValue = findSuppressableContentValue(config,
-                        type.getContentType(), beanDesc);
-                if (suppressableValue != null) {
-                    mapSer = mapSer.withContentInclusion(suppressableValue);
-                }
-                ser = mapSer;
+                ser = _checkMapContentInclusion(prov, beanDesc, mapSer);
             }
         }
         // [databind#120]: Allow post-processing
@@ -792,44 +791,176 @@
     }
 
     /**
-     *<p>
-     * NOTE: although return type is left opaque, it really needs to be
-     * <code>JsonInclude.Include</code> for things to work as expected.
+     * Helper method that does figures out content inclusion value to use, if any,
+     * and construct re-configured {@link MapSerializer} appropriately.
      *
-     * @since 2.5
+     * @since 2.9
      */
-    protected Object findSuppressableContentValue(SerializationConfig config,
-            JavaType contentType, BeanDescription beanDesc)
+    @SuppressWarnings("deprecation")
+    protected MapSerializer _checkMapContentInclusion(SerializerProvider prov,
+            BeanDescription beanDesc, MapSerializer mapSer)
         throws JsonMappingException
     {
-        /* 16-Apr-2016, tatu: Should this consider possible property-config overrides?
-         *    Quite possibly yes, but would need to carefully check that content type being
-         *    used is appropriate.
-         */
-        JsonInclude.Value inclV = beanDesc.findPropertyInclusion(config.getDefaultPropertyInclusion());
+        final JavaType contentType = mapSer.getContentType();
+        JsonInclude.Value inclV = _findInclusionWithContent(prov, beanDesc,
+                contentType, Map.class);
 
-        if (inclV == null) {
-            return null;
+        // Need to support global legacy setting, for now:
+        JsonInclude.Include incl = (inclV == null) ? JsonInclude.Include.USE_DEFAULTS : inclV.getContentInclusion();
+        if (incl == JsonInclude.Include.USE_DEFAULTS
+                || incl == JsonInclude.Include.ALWAYS) {
+            if (!prov.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES)) {
+                return mapSer.withContentInclusion(null, true);
+            }
+            return mapSer;
         }
-        JsonInclude.Include incl = inclV.getContentInclusion();
+
+        // NOTE: mostly copied from `PropertyBuilder`; would be nice to refactor
+        // but code is not identical nor are these types related
+        Object valueToSuppress;
+        boolean suppressNulls = true; // almost always, but possibly not with CUSTOM
+
         switch (incl) {
-        case USE_DEFAULTS: // means "dunno"
-            return null;
         case NON_DEFAULT:
-            // 19-Oct-2014, tatu: Not sure what this'd mean; so take it to mean "NON_EMPTY"...
-            // 11-Nov-2015, tatu: With 2.6, we did indeed revert to "NON_EMPTY", but that did
-            //    not go well, so with 2.7, we'll do this instead...
-            //   But not 100% sure if we ought to call new `JsonSerializer.findDefaultValue()`;
-            //   to do that, would need to locate said serializer
-//            incl = JsonInclude.Include.NON_EMPTY;
+            valueToSuppress = BeanUtil.getDefaultValue(contentType);
+            if (valueToSuppress != null) {
+                if (valueToSuppress.getClass().isArray()) {
+                    valueToSuppress = ArrayBuilders.getArrayComparator(valueToSuppress);
+                }
+            }
             break;
-        default:
-            // all other modes actually good as is, unless we'll find better ways
+        case NON_ABSENT: // new with 2.6, to support Guava/JDK8 Optionals
+            // and for referential types, also "empty", which in their case means "absent"
+            valueToSuppress = contentType.isReferenceType()
+                    ? MapSerializer.MARKER_FOR_EMPTY : null;
+            break;
+        case NON_EMPTY:
+            valueToSuppress = MapSerializer.MARKER_FOR_EMPTY;
+            break;
+        case CUSTOM: // new with 2.9
+            valueToSuppress = prov.includeFilterInstance(null, inclV.getContentFilter());
+            if (valueToSuppress == null) { // is this legal?
+                suppressNulls = true;
+            } else {
+                suppressNulls = prov.includeFilterSuppressNulls(valueToSuppress);
+            }
+            break;
+        case NON_NULL:
+        default: // should not matter but...
+            valueToSuppress = null;
             break;
         }
-        return incl;
+        return mapSer.withContentInclusion(valueToSuppress, suppressNulls);
     }
 
+    /**
+     * @since 2.9
+     */
+    protected JsonSerializer<?> buildMapEntrySerializer(SerializerProvider prov,
+            JavaType type, BeanDescription beanDesc, boolean staticTyping,
+            JavaType keyType, JavaType valueType)
+        throws JsonMappingException
+    {
+        // [databind#865]: Allow serialization "as POJO" -- note: to undo, declare
+        //   serialization as `Shape.NATURAL` instead; that's JSON Object too.
+        JsonFormat.Value formatOverride = prov.getDefaultPropertyFormat(Map.Entry.class);
+        JsonFormat.Value formatFromAnnotation = beanDesc.findExpectedFormat(null);
+        JsonFormat.Value format = JsonFormat.Value.merge(formatFromAnnotation, formatOverride);
+        if (format.getShape() == JsonFormat.Shape.OBJECT) {
+            return null;
+        }
+        MapEntrySerializer ser = new MapEntrySerializer(valueType, keyType, valueType,
+                staticTyping, createTypeSerializer(prov.getConfig(), valueType), null);
+
+        final JavaType contentType = ser.getContentType();
+        JsonInclude.Value inclV = _findInclusionWithContent(prov, beanDesc,
+                contentType, Map.Entry.class);
+
+        // Need to support global legacy setting, for now:
+        JsonInclude.Include incl = (inclV == null) ? JsonInclude.Include.USE_DEFAULTS : inclV.getContentInclusion();
+        if (incl == JsonInclude.Include.USE_DEFAULTS
+                || incl == JsonInclude.Include.ALWAYS) {
+            return ser;
+        }
+
+        // NOTE: mostly copied from `PropertyBuilder`; would be nice to refactor
+        // but code is not identical nor are these types related
+        Object valueToSuppress;
+        boolean suppressNulls = true; // almost always, but possibly not with CUSTOM
+
+        switch (incl) {
+        case NON_DEFAULT:
+            valueToSuppress = BeanUtil.getDefaultValue(contentType);
+            if (valueToSuppress != null) {
+                if (valueToSuppress.getClass().isArray()) {
+                    valueToSuppress = ArrayBuilders.getArrayComparator(valueToSuppress);
+                }
+            }
+            break;
+        case NON_ABSENT:
+            valueToSuppress = contentType.isReferenceType()
+                    ? MapSerializer.MARKER_FOR_EMPTY : null;
+            break;
+        case NON_EMPTY:
+            valueToSuppress = MapSerializer.MARKER_FOR_EMPTY;
+            break;
+        case CUSTOM:
+            valueToSuppress = prov.includeFilterInstance(null, inclV.getContentFilter());
+            if (valueToSuppress == null) { // is this legal?
+                suppressNulls = true;
+            } else {
+                suppressNulls = prov.includeFilterSuppressNulls(valueToSuppress);
+            }
+            break;
+        case NON_NULL:
+        default: // should not matter but...
+            valueToSuppress = null;
+            break;
+        }
+        return ser.withContentInclusion(valueToSuppress, suppressNulls);
+    }
+
+    /**
+     * Helper method used for finding inclusion definitions for structured
+     * container types like <code>Map</code>s and referential types
+     * (like <code>AtomicReference</code>).
+     *
+     * @param contentType Declared full content type of container
+     * @param configType Raw base type under which `configOverride`, if any, needs to be defined
+     */
+    protected JsonInclude.Value _findInclusionWithContent(SerializerProvider prov,
+            BeanDescription beanDesc,
+            JavaType contentType, Class<?> configType)
+        throws JsonMappingException
+    {
+        final SerializationConfig config = prov.getConfig();
+
+        // Defaulting gets complicated because we might have two distinct
+        //   axis to consider: Container type itself , and then value (content) type.
+        //  Start with Container-defaults, then use more-specific value override, if any.
+
+        // Start by getting global setting, overridden by Map-type-override
+        JsonInclude.Value inclV = beanDesc.findPropertyInclusion(config.getDefaultPropertyInclusion());
+        inclV = config.getDefaultPropertyInclusion(configType, inclV);
+
+        // and then merge content-type overrides, if any. But note that there's
+        // content-to-value inclusion shift we have to do
+        JsonInclude.Value valueIncl = config.getDefaultPropertyInclusion(contentType.getRawClass(), null);
+
+        if (valueIncl != null) {
+            switch (valueIncl.getValueInclusion()) {
+            case USE_DEFAULTS:
+                break;
+            case CUSTOM:
+                inclV = inclV.withContentFilter(valueIncl.getContentFilter());
+                break;
+            default:
+                inclV = inclV.withContentInclusion(valueIncl.getValueInclusion());
+            }
+        }
+        return inclV;
+    }
+    
     /*
     /**********************************************************
     /* Factory methods, for Arrays
@@ -847,7 +978,7 @@
         throws JsonMappingException
     {
         // 25-Jun-2015, tatu: Note that unlike with Collection(Like) and Map(Like) types, array
-        //   types can not be annotated (in theory I guess we could have mix-ins but... ?)
+        //   types cannot be annotated (in theory I guess we could have mix-ins but... ?)
         //   so we need not do primary annotation lookup here.
         //   So all we need is (1) Custom, (2) Default array serializers
         SerializationConfig config = prov.getConfig();
@@ -888,6 +1019,96 @@
 
     /*
     /**********************************************************
+    /* Factory methods for Reference types
+    /* (demoted from BeanSF down here in 2.9)
+    /**********************************************************
+     */
+
+    /**
+     * @since 2.7
+     */
+    public JsonSerializer<?> findReferenceSerializer(SerializerProvider prov, ReferenceType refType,
+            BeanDescription beanDesc, boolean staticTyping)
+        throws JsonMappingException
+    {
+        JavaType contentType = refType.getContentType(); 
+        TypeSerializer contentTypeSerializer = contentType.getTypeHandler();
+        final SerializationConfig config = prov.getConfig();
+        if (contentTypeSerializer == null) {
+            contentTypeSerializer = createTypeSerializer(config, contentType);
+        }
+        JsonSerializer<Object> contentSerializer = contentType.getValueHandler();
+        for (Serializers serializers : customSerializers()) {
+            JsonSerializer<?> ser = serializers.findReferenceSerializer(config, refType, beanDesc,
+                    contentTypeSerializer, contentSerializer);
+            if (ser != null) {
+                return ser;
+            }
+        }
+        if (refType.isTypeOrSubTypeOf(AtomicReference.class)) {
+            return buildAtomicReferenceSerializer(prov, refType, beanDesc, staticTyping,
+                    contentTypeSerializer, contentSerializer);
+        }
+        return null;
+    }
+
+    protected JsonSerializer<?> buildAtomicReferenceSerializer(SerializerProvider prov,
+            ReferenceType refType, BeanDescription beanDesc, boolean staticTyping,
+            TypeSerializer contentTypeSerializer, JsonSerializer<Object> contentSerializer)
+        throws JsonMappingException
+    {
+        final JavaType contentType = refType.getReferencedType();
+        JsonInclude.Value inclV = _findInclusionWithContent(prov, beanDesc,
+                contentType, AtomicReference.class);
+        
+        // Need to support global legacy setting, for now:
+        JsonInclude.Include incl = (inclV == null) ? JsonInclude.Include.USE_DEFAULTS : inclV.getContentInclusion();
+        Object valueToSuppress;
+        boolean suppressNulls;
+
+        if (incl == JsonInclude.Include.USE_DEFAULTS
+                || incl == JsonInclude.Include.ALWAYS) {
+            valueToSuppress = null;
+            suppressNulls = false;
+        } else {
+            suppressNulls = true;
+            switch (incl) {
+            case NON_DEFAULT:
+                valueToSuppress = BeanUtil.getDefaultValue(contentType);
+                if (valueToSuppress != null) {
+                    if (valueToSuppress.getClass().isArray()) {
+                        valueToSuppress = ArrayBuilders.getArrayComparator(valueToSuppress);
+                    }
+                }
+                break;
+            case NON_ABSENT:
+                valueToSuppress = contentType.isReferenceType()
+                        ? MapSerializer.MARKER_FOR_EMPTY : null;
+                break;
+            case NON_EMPTY:
+                valueToSuppress = MapSerializer.MARKER_FOR_EMPTY;
+                break;
+            case CUSTOM:
+                valueToSuppress = prov.includeFilterInstance(null, inclV.getContentFilter());
+                if (valueToSuppress == null) { // is this legal?
+                    suppressNulls = true;
+                } else {
+                    suppressNulls = prov.includeFilterSuppressNulls(valueToSuppress);
+                }
+                break;
+            case NON_NULL:
+            default: // should not matter but...
+                valueToSuppress = null;
+                break;
+            }
+        }
+        AtomicReferenceSerializer ser = new AtomicReferenceSerializer(refType, staticTyping,
+                contentTypeSerializer, contentSerializer);
+        return ser.withContentInclusion(valueToSuppress, suppressNulls);
+    }
+
+    /*
+    /**********************************************************
     /* Factory methods, for non-container types
     /**********************************************************
      */
@@ -914,18 +1135,6 @@
         return new IterableSerializer(valueType, staticTyping, createTypeSerializer(config, valueType));
     }
 
-    /**
-     * @since 2.5
-     */
-    protected JsonSerializer<?> buildMapEntrySerializer(SerializationConfig config,
-            JavaType type, BeanDescription beanDesc, boolean staticTyping,
-            JavaType keyType, JavaType valueType)
-        throws JsonMappingException
-    {
-        return new MapEntrySerializer(valueType, keyType, valueType,
-                staticTyping, createTypeSerializer(config, valueType), null);
-    }
-
     protected JsonSerializer<?> buildEnumSerializer(SerializationConfig config,
             JavaType type, BeanDescription beanDesc)
         throws JsonMappingException
@@ -1013,7 +1222,7 @@
     protected boolean usesStaticTyping(SerializationConfig config,
             BeanDescription beanDesc, TypeSerializer typeSer)
     {
-        /* 16-Aug-2010, tatu: If there is a (value) type serializer, we can not force
+        /* 16-Aug-2010, tatu: If there is a (value) type serializer, we cannot force
          *    static typing; that would make it impossible to handle expected subtypes
          */
         if (typeSer != null) {
@@ -1027,6 +1236,8 @@
         return config.isEnabled(MapperFeature.USE_STATIC_TYPING);
     }
 
+    // Commented out in 2.9
+    /*
     protected Class<?> _verifyAsClass(Object src, String methodName, Class<?> noneClass)
     {
         if (src == null) {
@@ -1041,4 +1252,5 @@
         }
         return cls;
     }
+    */
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/BeanPropertyWriter.java b/src/main/java/com/fasterxml/jackson/databind/ser/BeanPropertyWriter.java
index 49c87af..51cb1c2 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/BeanPropertyWriter.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/BeanPropertyWriter.java
@@ -21,6 +21,7 @@
 import com.fasterxml.jackson.databind.ser.impl.UnwrappingBeanPropertyWriter;
 import com.fasterxml.jackson.databind.ser.std.BeanSerializerBase;
 import com.fasterxml.jackson.databind.util.Annotations;
+import com.fasterxml.jackson.databind.util.ClassUtil;
 import com.fasterxml.jackson.databind.util.NameTransformer;
 
 /**
@@ -134,7 +135,7 @@
      */
 
     /**
-     * Serializer to use for writing out the value: null if it can not be known
+     * Serializer to use for writing out the value: null if it cannot be known
      * statically; non-null if it can.
      */
     protected JsonSerializer<Object> _serializer;
@@ -201,19 +202,23 @@
     /***********************************************************
      */
 
+    /**
+     * @since 2.9 (added `includeInViews` since 2.8)
+     */
     @SuppressWarnings("unchecked")
     public BeanPropertyWriter(BeanPropertyDefinition propDef,
             AnnotatedMember member, Annotations contextAnnotations,
-            JavaType declaredType, JsonSerializer<?> ser,
-            TypeSerializer typeSer, JavaType serType, boolean suppressNulls,
-            Object suppressableValue) {
+            JavaType declaredType,
+            JsonSerializer<?> ser, TypeSerializer typeSer, JavaType serType,
+            boolean suppressNulls, Object suppressableValue,
+            Class<?>[] includeInViews)
+    {
         super(propDef);
         _member = member;
         _contextAnnotations = contextAnnotations;
 
         _name = new SerializedString(propDef.getName());
         _wrapperName = propDef.getWrapperName();
-        _includeInViews = propDef.findViews();
 
         _declaredType = declaredType;
         _serializer = (JsonSerializer<Object>) ser;
@@ -239,6 +244,19 @@
 
         // this will be resolved later on, unless nulls are to be suppressed
         _nullSerializer = null;
+        _includeInViews = includeInViews;
+    }
+
+    @Deprecated // Since 2.9
+    public BeanPropertyWriter(BeanPropertyDefinition propDef,
+            AnnotatedMember member, Annotations contextAnnotations,
+            JavaType declaredType,
+            JsonSerializer<?> ser, TypeSerializer typeSer, JavaType serType,
+            boolean suppressNulls, Object suppressableValue)
+    {
+        this(propDef, member, contextAnnotations, declaredType,
+                ser, typeSer, serType, suppressNulls, suppressableValue,
+                null);
     }
 
     /**
@@ -372,8 +390,10 @@
      */
     public void assignSerializer(JsonSerializer<Object> ser) {
         // may need to disable check in future?
-        if (_serializer != null && _serializer != ser) {
-            throw new IllegalStateException("Can not override serializer");
+        if ((_serializer != null) && (_serializer != ser)) {
+            throw new IllegalStateException(String.format(
+                    "Cannot override _serializer: had a %s, trying to set to %s",
+                    ClassUtil.classNameOf(_serializer), ClassUtil.classNameOf(ser)));
         }
         _serializer = ser;
     }
@@ -384,7 +404,9 @@
     public void assignNullSerializer(JsonSerializer<Object> nullSer) {
         // may need to disable check in future?
         if ((_nullSerializer != null) && (_nullSerializer != nullSer)) {
-            throw new IllegalStateException("Can not override null serializer");
+            throw new IllegalStateException(String.format(
+                    "Cannot override _nullSerializer: had a %s, trying to set to %s",
+                    ClassUtil.classNameOf(_nullSerializer), ClassUtil.classNameOf(nullSer)));
         }
         _nullSerializer = nullSer;
     }
@@ -605,6 +627,7 @@
         return _cfgSerializationType;
     }
 
+    @Deprecated // since 2.9
     public Class<?> getRawSerializationType() {
         return (_cfgSerializationType == null) ? null : _cfgSerializationType
                 .getRawClass();
@@ -662,7 +685,7 @@
             SerializerProvider prov) throws Exception {
         // inlined 'get()'
         final Object value = (_accessorMethod == null) ? _field.get(bean)
-                : _accessorMethod.invoke(bean);
+                : _accessorMethod.invoke(bean, (Object[]) null);
 
         // Null handling is bit different, check that first
         if (value == null) {
@@ -734,7 +757,7 @@
             SerializerProvider prov) throws Exception {
         // inlined 'get()'
         final Object value = (_accessorMethod == null) ? _field.get(bean)
-                : _accessorMethod.invoke(bean);
+                : _accessorMethod.invoke(bean, (Object[]) null);
         if (value == null) { // nulls need specialized handling
             if (_nullSerializer != null) {
                 _nullSerializer.serialize(null, gen, prov);
@@ -890,7 +913,7 @@
      */
     public final Object get(Object bean) throws Exception {
         return (_accessorMethod == null) ? _field.get(bean) : _accessorMethod
-                .invoke(bean);
+                .invoke(bean, (Object[]) null);
     }
 
     /**
@@ -918,7 +941,7 @@
             // (something
             // OTHER than {@link BeanSerializerBase}
             if (ser instanceof BeanSerializerBase) {
-                prov.reportMappingProblem("Direct self-reference leading to cycle");
+                prov.reportBadDefinition(getType(), "Direct self-reference leading to cycle");
             }
         }
         return false;
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializer.java
index 7dc0d17..9813e4b 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializer.java
@@ -26,7 +26,7 @@
 public class BeanSerializer
     extends BeanSerializerBase
 {
-    private static final long serialVersionUID = -3618164443537292758L;
+    private static final long serialVersionUID = 29; // as per jackson 2.9
 
     /*
     /**********************************************************
@@ -112,7 +112,7 @@
     @Override
     protected BeanSerializerBase asArraySerializer()
     {
-        /* Can not:
+        /* Cannot:
          * 
          * - have Object Id (may be allowed in future)
          * - have "any getter"
@@ -156,7 +156,7 @@
         }
         gen.writeEndObject();
     }
-    
+
     /*
     /**********************************************************
     /* Standard methods
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerBuilder.java b/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerBuilder.java
index 95613e1..c89d380 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerBuilder.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerBuilder.java
@@ -37,7 +37,7 @@
     /**
      * Bean properties, in order of serialization
      */
-    protected List<BeanPropertyWriter> _properties;
+    protected List<BeanPropertyWriter> _properties = Collections.emptyList();
 
     /**
      * Optional array of filtered property writers; if null, no
@@ -66,13 +66,13 @@
      * type, if any.
      */
     protected ObjectIdWriter _objectIdWriter;
-    
+
     /*
     /**********************************************************
     /* Construction and setter methods
     /**********************************************************
      */
-    
+
     public BeanSerializerBuilder(BeanDescription beanDesc) {
         _beanDesc = beanDesc;
     }
@@ -105,10 +105,22 @@
         _properties = properties;
     }
 
+    /**
+     * @param properties Number and order of properties here MUST match that
+     *    of "regular" properties set earlier using {@link #setProperties(List)}; if not,
+     *    an {@link IllegalArgumentException} will be thrown
+     */
     public void setFilteredProperties(BeanPropertyWriter[] properties) {
+        if (properties != null) {
+            if (properties.length != _properties.size()) { // as per [databind#1612]
+                throw new IllegalArgumentException(String.format(
+                        "Trying to set %d filtered properties; must match length of non-filtered `properties` (%d)",
+                        properties.length, _properties.size()));
+            }
+        }
         _filteredProperties = properties;
     }
-    
+
     public void setAnyGetter(AnyGetterWriter anyGetter) {
         _anyGetter = anyGetter;
     }
@@ -185,6 +197,14 @@
                 }
             }
         }
+        // 27-Apr-2017, tatu: Verify that filtered-properties settings are compatible
+        if (_filteredProperties != null) {
+            if (_filteredProperties.length != _properties.size()) {
+                throw new IllegalStateException(String.format(
+"Mismatch between `properties` size (%d), `filteredProperties` (%s): should have as many (or `null` for latter)",
+_properties.size(), _filteredProperties.length));
+            }
+        }
         if (_anyGetter != null) {
             _anyGetter.fixAccess(_config);
         }
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerFactory.java b/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerFactory.java
index 921d67e..0040256 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerFactory.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerFactory.java
@@ -1,7 +1,6 @@
 package com.fasterxml.jackson.databind.ser;
 
 import java.util.*;
-import java.util.concurrent.atomic.AtomicReference;
 
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import com.fasterxml.jackson.annotation.ObjectIdGenerator;
@@ -9,7 +8,6 @@
 import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
 
 import com.fasterxml.jackson.databind.*;
-import com.fasterxml.jackson.databind.cfg.ConfigOverride;
 import com.fasterxml.jackson.databind.cfg.SerializerFactoryConfig;
 import com.fasterxml.jackson.databind.introspect.*;
 import com.fasterxml.jackson.databind.jsontype.NamedType;
@@ -18,7 +16,6 @@
 import com.fasterxml.jackson.databind.ser.impl.FilteredBeanPropertyWriter;
 import com.fasterxml.jackson.databind.ser.impl.ObjectIdWriter;
 import com.fasterxml.jackson.databind.ser.impl.PropertyBasedObjectIdGenerator;
-import com.fasterxml.jackson.databind.ser.std.AtomicReferenceSerializer;
 import com.fasterxml.jackson.databind.ser.std.MapSerializer;
 import com.fasterxml.jackson.databind.ser.std.StdDelegatingSerializer;
 import com.fasterxml.jackson.databind.type.ReferenceType;
@@ -100,7 +97,7 @@
          */
         if (getClass() != BeanSerializerFactory.class) {
             throw new IllegalStateException("Subtype of BeanSerializerFactory ("+getClass().getName()
-                    +") has not properly overridden method 'withAdditionalSerializers': can not instantiate subtype with "
+                    +") has not properly overridden method 'withAdditionalSerializers': cannot instantiate subtype with "
                     +"additional serializer definitions");
         }
         return new BeanSerializerFactory(config);
@@ -110,7 +107,7 @@
     protected Iterable<Serializers> customSerializers() {
         return _factoryConfig.serializers();
     }
-    
+
     /*
     /**********************************************************
     /* SerializerFactory impl
@@ -283,34 +280,6 @@
     }
 
     /**
-     * @since 2.7
-     */
-    public JsonSerializer<?> findReferenceSerializer(SerializerProvider prov, ReferenceType refType,
-            BeanDescription beanDesc, boolean staticTyping)
-        throws JsonMappingException
-    {
-        JavaType contentType = refType.getContentType(); 
-        TypeSerializer contentTypeSerializer = contentType.getTypeHandler();
-        final SerializationConfig config = prov.getConfig();
-        if (contentTypeSerializer == null) {
-            contentTypeSerializer = createTypeSerializer(config, contentType);
-        }
-        JsonSerializer<Object> contentSerializer = contentType.getValueHandler();
-        for (Serializers serializers : customSerializers()) {
-            JsonSerializer<?> ser = serializers.findReferenceSerializer(config, refType, beanDesc,
-                    contentTypeSerializer, contentSerializer);
-            if (ser != null) {
-                return ser;
-            }
-        }
-        if (refType.isTypeOrSubTypeOf(AtomicReference.class)) {
-            return new AtomicReferenceSerializer(refType, staticTyping,
-                    contentTypeSerializer, contentSerializer);
-        }
-        return null;
-    }
-
-    /**
      * Method called to create a type information serializer for values of given
      * non-container property
      * if one is needed. If not needed (no polymorphic handling configured), should
@@ -389,7 +358,7 @@
         // 05-Jul-2012, tatu: ... but we should be able to just return "unknown type" serializer, right?
         if (beanDesc.getBeanClass() == Object.class) {
             return prov.getUnknownTypeSerializer(Object.class);
-//            throw new IllegalArgumentException("Can not create bean serializer for Object.class");
+//            throw new IllegalArgumentException("Cannot create bean serializer for Object.class");
         }
         final SerializationConfig config = prov.getConfig();
         BeanSerializerBuilder builder = constructBeanSerializerBuilder(beanDesc);
@@ -423,10 +392,9 @@
             }
         }
 
-        /* And if Object Id is needed, some preparation for that as well: better
-         * do before view handling, mostly for the custom id case which needs
-         * access to a property
-         */
+        // And if Object Id is needed, some preparation for that as well: better
+        // do before view handling, mostly for the custom id case which needs
+        // access to a property
         builder.setObjectIdWriter(constructObjectIdHandler(prov, beanDesc, props));
         
         builder.setProperties(props);
@@ -450,7 +418,7 @@
             // TODO: can we find full PropertyName?
             PropertyName name = PropertyName.construct(anyGetter.getName());
             BeanProperty.Std anyProp = new BeanProperty.Std(name, valueType, null,
-                    beanDesc.getClassAnnotations(), anyGetter, PropertyMetadata.STD_OPTIONAL);
+                    anyGetter, PropertyMetadata.STD_OPTIONAL);
             builder.setAnyGetter(new AnyGetterWriter(anyProp, anyGetter, anySer));
         }
         // Next: need to gather view information, if any:
@@ -463,7 +431,13 @@
             }
         }
 
-        JsonSerializer<Object> ser = (JsonSerializer<Object>) builder.build();
+        JsonSerializer<Object> ser = null;
+        try {
+            ser = (JsonSerializer<Object>) builder.build();
+        } catch (RuntimeException e) {
+            prov.reportBadTypeDefinition(beanDesc, "Failed to construct BeanSerializer for %s: (%s) %s",
+                    beanDesc.getType(), e.getClass().getName(), e.getMessage());
+        }
         if (ser == null) {
             // If we get this far, there were no properties found, so no regular BeanSerializer
             // would be constructed. But, couple of exceptions.
@@ -494,14 +468,13 @@
             for (int i = 0, len = props.size() ;; ++i) {
                 if (i == len) {
                     throw new IllegalArgumentException("Invalid Object Id definition for "+beanDesc.getBeanClass().getName()
-                            +": can not find property with name '"+propName+"'");
+                            +": cannot find property with name '"+propName+"'");
                 }
                 BeanPropertyWriter prop = props.get(i);
                 if (propName.equals(prop.getName())) {
                     idProp = prop;
-                    /* Let's force it to be the first property to output
-                     * (although it may still get rearranged etc)
-                     */
+                    // Let's force it to be the first property to output
+                    // (although it may still get rearranged etc)
                     if (i > 0) {
                         props.remove(i);
                         props.add(0, idProp);
@@ -553,7 +526,7 @@
     
     /**
      * Helper method used to skip processing for types that we know
-     * can not be (i.e. are never consider to be) beans: 
+     * cannot be (i.e. are never consider to be) beans: 
      * things like primitives, Arrays, Enums, and proxy types.
      *<p>
      * Note that usually we shouldn't really be getting these sort of
@@ -689,6 +662,7 @@
      * Method that will apply by-type limitations (as per [JACKSON-429]);
      * by default this is based on {@link com.fasterxml.jackson.annotation.JsonIgnoreType}
      * annotation but can be supplied by module-provided introspectors too.
+     * Starting with 2.8 there are also "Config overrides" to consider.
      */
     protected void removeIgnorableTypes(SerializationConfig config, BeanDescription beanDesc,
             List<BeanPropertyDefinition> properties)
@@ -699,18 +673,19 @@
         while (it.hasNext()) {
             BeanPropertyDefinition property = it.next();
             AnnotatedMember accessor = property.getAccessor();
+            /* 22-Oct-2016, tatu: Looks like this removal is an important part of
+             *    processing, as taking it out will result in a few test failures...
+             *    But should probably be done somewhere else, not here?
+             */
             if (accessor == null) {
                 it.remove();
                 continue;
             }
-            Class<?> type = accessor.getRawType();
+            Class<?> type = property.getRawPrimaryType();
             Boolean result = ignores.get(type);
             if (result == null) {
                 // 21-Apr-2016, tatu: For 2.8, can specify config overrides
-                ConfigOverride override = config.findConfigOverride(type);
-                if (override != null) {
-                    result = override.getIsIgnoredType();
-                }
+                result = config.getConfigOverride(type).getIsIgnoredType();
                 if (result == null) {
                     BeanDescription desc = config.introspectClassAnnotations(type);
                     AnnotatedClass ac = desc.getClassInfo();
@@ -793,7 +768,7 @@
         final PropertyName name = propDef.getFullName();
         JavaType type = accessor.getType();
         BeanProperty.Std property = new BeanProperty.Std(name, type, propDef.getWrapperName(),
-                pb.getClassAnnotations(), accessor, propDef.getMetadata());
+                accessor, propDef.getMetadata());
 
         // Does member specify a serializer? If so, let's use it.
         JsonSerializer<?> annotatedSerializer = findSerializerFromAnnotation(prov,
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/ContainerSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/ContainerSerializer.java
index 637150a..c7d4bea 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/ContainerSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/ContainerSerializer.java
@@ -90,16 +90,7 @@
     /**********************************************************
      */
 
-    /* Overridden as abstract, to force re-implementation; necessary for all
-     * collection types.
-     */
-    @Override
-    @Deprecated
-    public boolean isEmpty(T value) {
-        return isEmpty(null, value);
-    }
-
-    // since 2.5: should be declared abstract in future (2.6)
+// since 2.5: should be declared abstract in future (2.9?)
 //    @Override
 //    public abstract boolean isEmpty(SerializerProvider prov, T value);
 
@@ -111,6 +102,10 @@
      * like "getElementCount()" method, this would not work well for
      * containers that do not keep track of size (like linked lists may
      * not).
+     *<p>
+     * Note, too, that as of now (2.9) this method is only called by serializer
+     * itself; and specifically is not used for non-array/collection types
+     * like <code>Map</code> or <code>Map.Entry</code> instances.
      */
     public abstract boolean hasSingleElement(T value);
 
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/ContextualSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/ContextualSerializer.java
index 6f7091d..b8be372 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/ContextualSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/ContextualSerializer.java
@@ -27,7 +27,7 @@
      * @param prov Serializer provider to use for accessing config, other serializers
      * @param property Method or field that represents the property
      *   (and is used to access value to serialize).
-     *   Should be available; but there may be cases where caller can not provide it and
+     *   Should be available; but there may be cases where caller cannot provide it and
      *   null is passed instead (in which case impls usually pass 'this' serializer as is)
      * 
      * @return Serializer to use for serializing values of specified property;
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/DefaultSerializerProvider.java b/src/main/java/com/fasterxml/jackson/databind/ser/DefaultSerializerProvider.java
index ed8e965..549a256 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/DefaultSerializerProvider.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/DefaultSerializerProvider.java
@@ -9,6 +9,7 @@
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
 import com.fasterxml.jackson.databind.introspect.Annotated;
+import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
 import com.fasterxml.jackson.databind.jsonschema.SchemaAware;
 import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
@@ -100,7 +101,8 @@
      */
     
     @Override
-    public JsonSerializer<Object> serializerInstance(Annotated annotated, Object serDef) throws JsonMappingException
+    public JsonSerializer<Object> serializerInstance(Annotated annotated, Object serDef)
+            throws JsonMappingException
     {
         if (serDef == null) {
             return null;
@@ -110,11 +112,11 @@
         if (serDef instanceof JsonSerializer) {
             ser = (JsonSerializer<?>) serDef;
         } else {
-            /* Alas, there's no way to force return type of "either class
-             * X or Y" -- need to throw an exception after the fact
-             */
+            // Alas, there's no way to force return type of "either class
+            // X or Y" -- need to throw an exception after the fact
             if (!(serDef instanceof Class)) {
-                throw new IllegalStateException("AnnotationIntrospector returned serializer definition of type "
+                reportBadDefinition(annotated.getType(),
+                        "AnnotationIntrospector returned serializer definition of type "
                         +serDef.getClass().getName()+"; expected type JsonSerializer or Class<JsonSerializer> instead");
             }
             Class<?> serClass = (Class<?>)serDef;
@@ -123,7 +125,8 @@
                 return null;
             }
             if (!JsonSerializer.class.isAssignableFrom(serClass)) {
-                throw new IllegalStateException("AnnotationIntrospector returned Class "
+                reportBadDefinition(annotated.getType(),
+                        "AnnotationIntrospector returned Class "
                         +serClass.getName()+"; expected Class<JsonSerializer>");
             }
             HandlerInstantiator hi = _config.getHandlerInstantiator();
@@ -136,6 +139,41 @@
         return (JsonSerializer<Object>) _handleResolvable(ser);
     }
 
+    @Override
+    public Object includeFilterInstance(BeanPropertyDefinition forProperty,
+            Class<?> filterClass)
+    {
+        if (filterClass == null) {
+            return null;
+        }
+        HandlerInstantiator hi = _config.getHandlerInstantiator();
+        Object filter = (hi == null) ? null : hi.includeFilterInstance(_config, forProperty, filterClass);
+        if (filter == null) {
+            filter = ClassUtil.createInstance(filterClass,
+                    _config.canOverrideAccessModifiers());
+        }
+        return filter;
+    }
+
+    @Override
+    public boolean includeFilterSuppressNulls(Object filter) throws JsonMappingException
+    {
+        if (filter == null) {
+            return true;
+        }
+        // should let filter decide what to do with nulls:
+        // But just case, let's handle unexpected (from our perspective) problems explicitly
+        try {
+            return filter.equals(null);
+        } catch (Throwable t) {
+            String msg = String.format(
+"Problem determining whether filter of type '%s' should filter out `null` values: (%s) %s",
+filter.getClass().getName(), t.getClass().getName(), t.getMessage());
+            reportBadDefinition(filter.getClass(), msg, t);
+            return false; // never gets here
+        }
+    }
+
     /*
     /**********************************************************
     /* Object Id handling
@@ -265,43 +303,20 @@
             _serializeNull(gen);
             return;
         }
-        Class<?> cls = value.getClass();
+        final Class<?> cls = value.getClass();
         // true, since we do want to cache root-level typed serializers (ditto for null property)
         final JsonSerializer<Object> ser = findTypedValueSerializer(cls, true, null);
-
-        // Ok: should we wrap result in an additional property ("root name")?
-        final boolean wrap;
         PropertyName rootName = _config.getFullRootName();
-
         if (rootName == null) { // not explicitly specified
-            wrap = _config.isEnabled(SerializationFeature.WRAP_ROOT_VALUE);
-            if (wrap) {
-                gen.writeStartObject();
-                PropertyName pname = _config.findRootName(value.getClass());
-                gen.writeFieldName(pname.simpleAsEncoded(_config));
+            if (_config.isEnabled(SerializationFeature.WRAP_ROOT_VALUE)) {
+                _serialize(gen, value, ser, _config.findRootName(cls));
+                return;
             }
-        } else if (rootName.isEmpty()) {
-            wrap = false;
-        } else { // [JACKSON-764]
-            // empty String means explicitly disabled; non-empty that it is enabled
-            wrap = true;
-            gen.writeStartObject();
-            gen.writeFieldName(rootName.getSimpleName());
+        } else if (!rootName.isEmpty()) {
+            _serialize(gen, value, ser, rootName);
+            return;
         }
-        try {
-            ser.serialize(value, gen, this);
-            if (wrap) {
-                gen.writeEndObject();
-            }
-        } catch (IOException ioe) { // As per [JACKSON-99], pass IOException and subtypes as-is
-            throw ioe;
-        } catch (Exception e) { // but wrap RuntimeExceptions, to get path information
-            String msg = e.getMessage();
-            if (msg == null) {
-                msg = "[no message for "+e.getClass().getName()+"]";
-            }
-            throw new JsonMappingException(gen, msg, e);
-        }
+        _serialize(gen, value, ser);
     }
 
     /**
@@ -328,39 +343,17 @@
         }
         // root value, not reached via property:
         JsonSerializer<Object> ser = findTypedValueSerializer(rootType, true, null);
-
-        // Ok: should we wrap result in an additional property ("root name")?
-        final boolean wrap;
         PropertyName rootName = _config.getFullRootName();
         if (rootName == null) { // not explicitly specified
-            wrap = _config.isEnabled(SerializationFeature.WRAP_ROOT_VALUE);
-            if (wrap) {
-                gen.writeStartObject();
-                PropertyName pname = _config.findRootName(value.getClass());
-                gen.writeFieldName(pname.simpleAsEncoded(_config));
+            if (_config.isEnabled(SerializationFeature.WRAP_ROOT_VALUE)) {
+                _serialize(gen, value, ser, _config.findRootName(rootType));
+                return;
             }
-        } else if (rootName.isEmpty()) {
-            wrap = false;
-        } else {
-            // empty String means explicitly disabled; non-empty that it is enabled
-            wrap = true;
-            gen.writeStartObject();
-            gen.writeFieldName(rootName.getSimpleName());
+        } else if (!rootName.isEmpty()) {
+            _serialize(gen, value, ser, rootName);
+            return;
         }
-        try {
-            ser.serialize(value, gen, this);
-            if (wrap) {
-                gen.writeEndObject();
-            }
-        } catch (IOException ioe) { // no wrapping for IO (and derived)
-            throw ioe;
-        } catch (Exception e) { // but others do need to be, to get path etc
-            String msg = e.getMessage();
-            if (msg == null) {
-                msg = "[no message for "+e.getClass().getName()+"]";
-            }
-            reportMappingProblem(e, msg);
-        }
+        _serialize(gen, value, ser);
     }
 
     /**
@@ -391,41 +384,20 @@
         if (ser == null) {
             ser = findTypedValueSerializer(rootType, true, null);
         }
-        // Ok: should we wrap result in an additional property ("root name")?
-        final boolean wrap;
         PropertyName rootName = _config.getFullRootName();
         if (rootName == null) { // not explicitly specified
-            // [JACKSON-163]
-            wrap = _config.isEnabled(SerializationFeature.WRAP_ROOT_VALUE);
-            if (wrap) {
-                gen.writeStartObject();
-                PropertyName pname = (rootType == null)
+            if (_config.isEnabled(SerializationFeature.WRAP_ROOT_VALUE)) {
+                rootName = (rootType == null)
                         ? _config.findRootName(value.getClass())
                         : _config.findRootName(rootType);
-                gen.writeFieldName(pname.simpleAsEncoded(_config));
+                _serialize(gen, value, ser, rootName);
+                return;
             }
-        } else if (rootName.isEmpty()) {
-            wrap = false;
-        } else { // [JACKSON-764]
-            // empty String means explicitly disabled; non-empty that it is enabled
-            wrap = true;
-            gen.writeStartObject();
-            gen.writeFieldName(rootName.getSimpleName());
+        } else if (!rootName.isEmpty()) {
+            _serialize(gen, value, ser, rootName);
+            return;
         }
-        try {
-            ser.serialize(value, gen, this);
-            if (wrap) {
-                gen.writeEndObject();
-            }
-        } catch (IOException ioe) { // no wrapping for IO (and derived)
-            throw ioe;
-        } catch (Exception e) { // but others do need to be, to get path etc
-            String msg = e.getMessage();
-            if (msg == null) {
-                msg = "[no message for "+e.getClass().getName()+"]";
-            }
-            reportMappingProblem(e, msg);
-        }
+        _serialize(gen, value, ser);
     }
 
     /**
@@ -481,26 +453,34 @@
             if (wrap) {
                 gen.writeEndObject();
             }
-        } catch (IOException ioe) { // no wrapping for IO (and derived)
-            throw ioe;
-        } catch (Exception e) { // but others do need to be, to get path etc
-            String msg = e.getMessage();
-            if (msg == null) {
-                msg = "[no message for "+e.getClass().getName()+"]";
-            }
-            reportMappingProblem(e, msg);
+        } catch (Exception e) {
+            throw _wrapAsIOE(gen, e);
         }
     }
 
-    /**
-     * @deprecated since 2.6; remove from 2.7 or later
-     */
-    @Deprecated
-    public void serializePolymorphic(JsonGenerator gen, Object value, TypeSerializer typeSer)
-            throws IOException
+    private final void _serialize(JsonGenerator gen, Object value,
+            JsonSerializer<Object> ser, PropertyName rootName)
+        throws IOException
     {
-        JavaType t = (value == null) ? null : _config.constructType(value.getClass());
-        serializePolymorphic(gen, value, t, null, typeSer);
+        try {
+            gen.writeStartObject();
+            gen.writeFieldName(rootName.simpleAsEncoded(_config));
+            ser.serialize(value, gen, this);
+            gen.writeEndObject();
+        } catch (Exception e) {
+            throw _wrapAsIOE(gen, e);
+        }
+    }
+
+    private final void _serialize(JsonGenerator gen, Object value,
+            JsonSerializer<Object> ser)
+        throws IOException
+    {
+        try {
+            ser.serialize(value, gen, this);
+        } catch (Exception e) {
+            throw _wrapAsIOE(gen, e);
+        }
     }
 
     /**
@@ -513,16 +493,22 @@
         JsonSerializer<Object> ser = getDefaultNullValueSerializer();
         try {
             ser.serialize(null, gen, this);
-        } catch (IOException ioe) { // no wrapping for IO (and derived)
-            throw ioe;
-        } catch (Exception e) { // but others do need to be, to get path etc
-            String msg = e.getMessage();
-            if (msg == null) {
-                msg = "[no message for "+e.getClass().getName()+"]";
-            }
-            reportMappingProblem(e, msg);
+        } catch (Exception e) {
+            throw _wrapAsIOE(gen, e);
         }
     }
+
+    private IOException _wrapAsIOE(JsonGenerator g, Exception e) {
+        if (e instanceof IOException) {
+            return (IOException) e;
+        }
+        String msg = e.getMessage();
+        if (msg == null) {
+            msg = "[no message for "+e.getClass().getName()+"]";
+        }
+        return new JsonMappingException(g, msg, e);
+    }
+
     /*
     /********************************************************
     /* Access to caching details
@@ -593,9 +579,6 @@
     public com.fasterxml.jackson.databind.jsonschema.JsonSchema generateJsonSchema(Class<?> type)
         throws JsonMappingException
     {
-        if (type == null) {
-            throw new IllegalArgumentException("A class must be provided");
-        }
         /* no need for embedded type information for JSON schema generation (all
          * type information it needs is accessible via "untyped" serializer)
          */
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/PropertyBuilder.java b/src/main/java/com/fasterxml/jackson/databind/ser/PropertyBuilder.java
index 6363421..43602e3 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/PropertyBuilder.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/PropertyBuilder.java
@@ -48,7 +48,7 @@
      * @since 2.8
      */
     final protected boolean _useRealPropertyDefaults;
-    
+
     public PropertyBuilder(SerializationConfig config, BeanDescription beanDesc)
     {
         _config = config;
@@ -56,8 +56,10 @@
         // 08-Sep-2016, tatu: This gets tricky, with 3 levels of definitions:
         //  (a) global default inclusion
         //  (b) per-type default inclusion (from annotation or config overrides;
-        //     latter having precedence
-        //  Cc) per-property override
+        //     config override having precedence)
+        //  (c) per-property override (from annotation on specific property or
+        //     config overrides per type of property;
+        //     annotation having precedence)
         //
         //  and not only requiring merging, but also considering special handling
         //  for NON_DEFAULT in case of (b) (vs (a) or (c))
@@ -86,7 +88,6 @@
      *    to use for contained values (only used for properties that are
      *    of container type)
      */
-    @SuppressWarnings("deprecation")
     protected BeanPropertyWriter buildWriter(SerializerProvider prov,
             BeanPropertyDefinition propDef, JavaType declaredType, JsonSerializer<?> ser,
             TypeSerializer typeSer, TypeSerializer contentTypeSer,
@@ -98,15 +99,17 @@
         try {
             serializationType = findSerializationType(am, defaultUseStaticTyping, declaredType);
         } catch (JsonMappingException e) {
+            if (propDef == null) {
+                return prov.reportBadDefinition(declaredType, e.getMessage());
+            }
             return prov.reportBadPropertyDefinition(_beanDesc, propDef, e.getMessage());
         }
 
         // Container types can have separate type serializers for content (value / element) type
         if (contentTypeSer != null) {
-            /* 04-Feb-2010, tatu: Let's force static typing for collection, if there is
-             *    type information for contents. Should work well (for JAXB case); can be
-             *    revisited if this causes problems.
-             */
+            // 04-Feb-2010, tatu: Let's force static typing for collection, if there is
+            //    type information for contents. Should work well (for JAXB case); can be
+            //    revisited if this causes problems.
             if (serializationType == null) {
 //                serializationType = TypeFactory.type(am.getGenericType(), _beanDesc.getType());
                 serializationType = declaredType;
@@ -127,21 +130,29 @@
         // 12-Jul-2016, tatu: [databind#1256] Need to make sure we consider type refinement
         JavaType actualType = (serializationType == null) ? declaredType : serializationType;
         
+        // 17-Mar-2017: [databind#1522] Allow config override per property type
+        AnnotatedMember accessor = propDef.getAccessor();
+        if (accessor == null) {
+            // neither Setter nor ConstructorParameter are expected here
+            return prov.reportBadPropertyDefinition(_beanDesc, propDef,
+                    "could not determine property type");
+        }
+        Class<?> rawPropertyType = accessor.getRawType();
+
         // 17-Aug-2016, tatu: Default inclusion covers global default (for all types), as well
         //   as type-default for enclosing POJO. What we need, then, is per-type default (if any)
         //   for declared property type... and finally property annotation overrides
-        JsonInclude.Value inclV = _config.getDefaultPropertyInclusion(actualType.getRawClass(),
-                _defaultInclusion);
+        JsonInclude.Value inclV = _config.getDefaultInclusion(actualType.getRawClass(),
+                rawPropertyType, _defaultInclusion);
 
         // property annotation override
         
         inclV = inclV.withOverrides(propDef.findInclusion());
-        JsonInclude.Include inclusion = inclV.getValueInclusion();
 
+        JsonInclude.Include inclusion = inclV.getValueInclusion();
         if (inclusion == JsonInclude.Include.USE_DEFAULTS) { // should not occur but...
             inclusion = JsonInclude.Include.ALWAYS;
         }
-        
         switch (inclusion) {
         case NON_DEFAULT:
             // 11-Nov-2015, tatu: This is tricky because semantics differ between cases,
@@ -151,7 +162,7 @@
             // First: case of class/type specifying it; try to find POJO property defaults
             Object defaultBean;
 
-            // 16-Oct-2016, tatu: Note: if we can not for some reason create "default instance",
+            // 16-Oct-2016, tatu: Note: if we cannot for some reason create "default instance",
             //    revert logic to the case of general/per-property handling, so both
             //    type-default AND null are to be excluded.
             //    (as per [databind#1417]
@@ -166,7 +177,7 @@
                     _throwWrapped(e, propDef.getName(), defaultBean);
                 }
             } else {
-                valueToSuppress = getDefaultValue(actualType);
+                valueToSuppress = BeanUtil.getDefaultValue(actualType);
                 suppressNulls = true;
             }
             if (valueToSuppress == null) {
@@ -191,21 +202,33 @@
             // but possibly also 'empty' values:
             valueToSuppress = BeanPropertyWriter.MARKER_FOR_EMPTY;
             break;
+        case CUSTOM: // new with 2.9
+            valueToSuppress = prov.includeFilterInstance(propDef, inclV.getValueFilter());
+            if (valueToSuppress == null) { // is this legal?
+                suppressNulls = true;
+            } else {
+                suppressNulls = prov.includeFilterSuppressNulls(valueToSuppress);
+            }
+            break;
         case NON_NULL:
             suppressNulls = true;
             // fall through
         case ALWAYS: // default
         default:
-            // we may still want to suppress empty collections, as per [JACKSON-254]:
+            // we may still want to suppress empty collections
             if (actualType.isContainerType()
                     && !_config.isEnabled(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS)) {
                 valueToSuppress = BeanPropertyWriter.MARKER_FOR_EMPTY;
             }
             break;
         }
+        Class<?>[] views = propDef.findViews();
+        if (views == null) {
+            views = _beanDesc.findDefaultViews();
+        }
         BeanPropertyWriter bpw = new BeanPropertyWriter(propDef,
                 am, _beanDesc.getClassAnnotations(), declaredType,
-                ser, typeSer, serializationType, suppressNulls, valueToSuppress);
+                ser, typeSer, serializationType, suppressNulls, valueToSuppress, views);
 
         // How about custom null serializer?
         Object serDef = _annotationIntrospector.findNullSerializer(am);
@@ -294,7 +317,7 @@
                 // 06-Nov-2015, tatu: As per [databind#998], do not fail.
                 /*
                 Class<?> cls = _beanDesc.getClassInfo().getAnnotated();
-                throw new IllegalArgumentException("Class "+cls.getName()+" has no default constructor; can not instantiate default bean value to support 'properties=JsonSerialize.Inclusion.NON_DEFAULT' annotation");
+                throw new IllegalArgumentException("Class "+cls.getName()+" has no default constructor; cannot instantiate default bean value to support 'properties=JsonSerialize.Inclusion.NON_DEFAULT' annotation");
                  */
 
                 // And use a marker
@@ -317,10 +340,10 @@
      * {@link com.fasterxml.jackson.annotation.JsonInclude.Include#NON_EMPTY} requires special handling.
      *
      * @since 2.7
-     * @deprecated Since 2.8.5 since this will not allow determining difference between "no default instance"
+     * @deprecated Since 2.9 since this will not allow determining difference between "no default instance"
      *    case and default being `null`.
      */
-    @Deprecated // since 2.8.5
+    @Deprecated // since 2.9
     protected Object getPropertyDefaultValue(String name, AnnotatedMember member,
             JavaType type)
     {
@@ -336,37 +359,13 @@
     }
 
     /**
-     * Accessor used to find out "default value" to use for comparing values to
-     * serialize, to determine whether to exclude value from serialization with
-     * inclusion type of {@link com.fasterxml.jackson.annotation.JsonInclude.Include#NON_DEFAULT}.
-     *<p>
-     * Default logic is such that for primitives and wrapper types for primitives, expected
-     * defaults (0 for `int` and `java.lang.Integer`) are returned; for Strings, empty String,
-     * and for structured (Maps, Collections, arrays) and reference types, criteria
-     * {@link com.fasterxml.jackson.annotation.JsonInclude.Include#NON_DEFAULT}
-     * is used.
-     *
-     * @since 2.7
+     * @deprecated Since 2.9
      */
-    protected Object getDefaultValue(JavaType type)
-    {
-        // 06-Nov-2015, tatu: Returning null is fine for Object types; but need special
-        //   handling for primitives since they are never passed as nulls.
-        Class<?> cls = type.getRawClass();
-
-        Class<?> prim = ClassUtil.primitiveType(cls);
-        if (prim != null) {
-            return ClassUtil.defaultValue(prim);
-        }
-        if (type.isContainerType() || type.isReferenceType()) {
-            return JsonInclude.Include.NON_EMPTY;
-        }
-        if (cls == String.class) {
-            return "";
-        }
-        return null;
+    @Deprecated // since 2.9
+    protected Object getDefaultValue(JavaType type) {
+        return BeanUtil.getDefaultValue(type);
     }
-    
+
     /*
     /**********************************************************
     /* Helper methods for exception handling
@@ -379,8 +378,8 @@
         while (t.getCause() != null) {
             t = t.getCause();
         }
-        if (t instanceof Error) throw (Error) t;
-        if (t instanceof RuntimeException) throw (RuntimeException) t;
+        ClassUtil.throwIfError(t);
+        ClassUtil.throwIfRTE(t);
         throw new IllegalArgumentException("Failed to get property '"+propName+"' of default "+defaultBean.getClass().getName()+" instance");
     }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/PropertyFilter.java b/src/main/java/com/fasterxml/jackson/databind/ser/PropertyFilter.java
index a77c540..89f32ef 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/PropertyFilter.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/PropertyFilter.java
@@ -34,17 +34,17 @@
      * Typical implementation is something like:
      *<pre>
      * if (include(writer)) {
-     *      writer.serializeAsField(pojo, jgen, prov);
+     *      writer.serializeAsField(pojo, gen, prov);
      * }
      *</pre>
      * 
      * @param pojo Object that contains property value to serialize
-     * @param jgen Generator use for serializing value
+     * @param gen Generator use for serializing value
      * @param prov Provider that can be used for accessing dynamic aspects of serialization
      *    processing
      * @param writer Object called to do actual serialization of the field, if not filtered out
      */
-    public void serializeAsField(Object pojo, JsonGenerator jgen, SerializerProvider prov,
+    public void serializeAsField(Object pojo, JsonGenerator gen, SerializerProvider prov,
             PropertyWriter writer)
         throws Exception;
 
@@ -58,17 +58,17 @@
      * Typical implementation is something like:
      *<pre>
      * if (include(writer)) {
-     *      writer.serializeAsElement(pojo, jgen, prov);
+     *      writer.serializeAsElement(pojo, gen, prov);
      * }
      *</pre>
      * 
      * @param elementValue Element value being serializerd
-     * @param jgen Generator use for serializing value
+     * @param gen Generator use for serializing value
      * @param prov Provider that can be used for accessing dynamic aspects of serialization
      *    processing
      * @param writer Object called to do actual serialization of the field, if not filtered out
      */
-    public void serializeAsElement(Object elementValue, JsonGenerator jgen, SerializerProvider prov,
+    public void serializeAsElement(Object elementValue, JsonGenerator gen, SerializerProvider prov,
             PropertyWriter writer)
         throws Exception;
     
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/VirtualBeanPropertyWriter.java b/src/main/java/com/fasterxml/jackson/databind/ser/VirtualBeanPropertyWriter.java
index 54b9d72..ad6cd35 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/VirtualBeanPropertyWriter.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/VirtualBeanPropertyWriter.java
@@ -52,13 +52,23 @@
     protected VirtualBeanPropertyWriter(BeanPropertyDefinition propDef,
             Annotations contextAnnotations, JavaType declaredType,
             JsonSerializer<?> ser, TypeSerializer typeSer, JavaType serType,
-            JsonInclude.Value inclusion)
+            JsonInclude.Value inclusion, Class<?>[] includeInViews)
     {
         super(propDef, propDef.getPrimaryMember(), contextAnnotations, declaredType,
                 ser, typeSer, serType,
-                _suppressNulls(inclusion), _suppressableValue(inclusion));
+                _suppressNulls(inclusion), _suppressableValue(inclusion),
+                includeInViews);
     }
 
+    @Deprecated // since 2.8
+    protected VirtualBeanPropertyWriter(BeanPropertyDefinition propDef,
+            Annotations contextAnnotations, JavaType declaredType,
+            JsonSerializer<?> ser, TypeSerializer typeSer, JavaType serType,
+            JsonInclude.Value inclusion)
+    {
+        this(propDef, contextAnnotations, declaredType, ser, typeSer, serType, inclusion, null);
+    }
+    
     protected VirtualBeanPropertyWriter(VirtualBeanPropertyWriter base) {
         super(base);
     }
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/AttributePropertyWriter.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/AttributePropertyWriter.java
index a1cd172..477f9ee 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/AttributePropertyWriter.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/AttributePropertyWriter.java
@@ -41,7 +41,9 @@
     {
         super(propDef, contextAnnotations, declaredType,
                 /* value serializer */ null, /* type serializer */ null, /* ser type */ null,
-                inclusion);
+                inclusion,
+                // 10-Oct-2016, tatu: Could enable per-view settings too in future
+                null);
         _attrName = attrName;
     }
 
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/BeanAsArraySerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/BeanAsArraySerializer.java
index 3f764ca..64ced81 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/BeanAsArraySerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/BeanAsArraySerializer.java
@@ -4,6 +4,8 @@
 import java.util.Set;
 
 import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.core.type.WritableTypeId;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
 import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
@@ -48,7 +50,7 @@
 
     /**
      * Serializer that would produce JSON Object version; used in
-     * cases where array output can not be used.
+     * cases where array output cannot be used.
      */
     protected final BeanSerializerBase _defaultSerializer;
     
@@ -134,18 +136,11 @@
             _serializeWithObjectId(bean, gen, provider, typeSer);
             return;
         }
-        String typeStr = (_typeId == null) ? null : _customTypeId(bean);
-        if (typeStr == null) {
-            typeSer.writeTypePrefixForArray(bean, gen);
-        } else {
-            typeSer.writeCustomTypePrefixForArray(bean, gen, typeStr);
-        }
+        gen.setCurrentValue(bean);
+        WritableTypeId typeIdDef = _typeIdDef(typeSer, bean, JsonToken.START_ARRAY);
+        typeSer.writeTypePrefix(gen, typeIdDef);
         serializeAsArray(bean, gen, provider);
-        if (typeStr == null) {
-            typeSer.writeTypeSuffixForArray(bean, gen);
-        } else {
-            typeSer.writeCustomTypeSuffixForArray(bean, gen, typeStr);
-        }
+        typeSer.writeTypeSuffix(gen, typeIdDef);
     }
 
     /**
@@ -209,7 +204,7 @@
                     prop.serializeAsElement(bean, gen, provider);
                 }
             }
-            // NOTE: any getters can not be supported either
+            // NOTE: any getters cannot be supported either
             //if (_anyGetterWriter != null) {
             //    _anyGetterWriter.getAndSerialize(bean, gen, provider);
             //}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/FilteredBeanPropertyWriter.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/FilteredBeanPropertyWriter.java
index ef7e635..e21d8ff 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/FilteredBeanPropertyWriter.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/FilteredBeanPropertyWriter.java
@@ -61,26 +61,26 @@
         }
         
         @Override
-        public void serializeAsField(Object bean, JsonGenerator jgen, SerializerProvider prov)
+        public void serializeAsField(Object bean, JsonGenerator gen, SerializerProvider prov)
             throws Exception
         {
             Class<?> activeView = prov.getActiveView();
             if (activeView == null || _view.isAssignableFrom(activeView)) {
-                _delegate.serializeAsField(bean, jgen, prov);
+                _delegate.serializeAsField(bean, gen, prov);
             } else {
-                _delegate.serializeAsOmittedField(bean, jgen, prov);
+                _delegate.serializeAsOmittedField(bean, gen, prov);
             }
         }
 
         @Override
-        public void serializeAsElement(Object bean, JsonGenerator jgen, SerializerProvider prov)
+        public void serializeAsElement(Object bean, JsonGenerator gen, SerializerProvider prov)
             throws Exception
         {
             Class<?> activeView = prov.getActiveView();
             if (activeView == null || _view.isAssignableFrom(activeView)) {
-                _delegate.serializeAsElement(bean, jgen, prov);
+                _delegate.serializeAsElement(bean, gen, prov);
             } else {
-                _delegate.serializeAsPlaceholder(bean, jgen, prov);
+                _delegate.serializeAsPlaceholder(bean, gen, prov);
             }
         }
 
@@ -127,58 +127,48 @@
         }
         
         @Override
-        public void serializeAsField(Object bean, JsonGenerator jgen, SerializerProvider prov)
+        public void serializeAsField(Object bean, JsonGenerator gen, SerializerProvider prov)
             throws Exception
         {
-            final Class<?> activeView = prov.getActiveView();
-            if (activeView != null) {
-                int i = 0, len = _views.length;
-                for (; i < len; ++i) {
-                    if (_views[i].isAssignableFrom(activeView)) break;
-                }
-                // not included, bail out:
-                if (i == len) {
-                    _delegate.serializeAsOmittedField(bean, jgen, prov);
-                    return;
-                }
+            if (_inView(prov.getActiveView())) {
+                _delegate.serializeAsField(bean, gen, prov);
+                return;
             }
-            _delegate.serializeAsField(bean, jgen, prov);
+            _delegate.serializeAsOmittedField(bean, gen, prov);
         }
 
         @Override
-        public void serializeAsElement(Object bean, JsonGenerator jgen, SerializerProvider prov)
+        public void serializeAsElement(Object bean, JsonGenerator gen, SerializerProvider prov)
             throws Exception
         {
-            final Class<?> activeView = prov.getActiveView();
-            if (activeView != null) {
-                int i = 0, len = _views.length;
-                for (; i < len; ++i) {
-                    if (_views[i].isAssignableFrom(activeView)) break;
-                }
-                // not included, bail out:
-                if (i == len) {
-                    _delegate.serializeAsPlaceholder(bean, jgen, prov);
-                    return;
-                }
+            if (_inView(prov.getActiveView())) {
+                _delegate.serializeAsElement(bean, gen, prov);
+                return;
             }
-            _delegate.serializeAsElement(bean, jgen, prov);
+            _delegate.serializeAsPlaceholder(bean, gen, prov);
         }
 
         @Override
         public void depositSchemaProperty(JsonObjectFormatVisitor v,
                 SerializerProvider provider) throws JsonMappingException
         {
-            Class<?> activeView = provider.getActiveView();
-            if (activeView != null) {
-                int i = 0, len = _views.length;
-                for (; i < len; ++i) {
-                    if (_views[i].isAssignableFrom(activeView)) break;
-                }
-                if (i == len) { // not match? Just don't deposit
-                    return;
+            if (_inView(provider.getActiveView())) {
+                super.depositSchemaProperty(v, provider);
+            }
+        }
+
+        private final boolean _inView(Class<?> activeView) 
+        {
+            if (activeView == null) {
+                return true;
+            }
+            final int len = _views.length;
+            for (int i = 0; i < len; ++i) {
+                if (_views[i].isAssignableFrom(activeView)) {
+                    return true;
                 }
             }
-            super.depositSchemaProperty(v, provider);
+            return false;
         }
     }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/IndexedListSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/IndexedListSerializer.java
index ffa5742..44f5c87 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/IndexedListSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/IndexedListSerializer.java
@@ -13,7 +13,7 @@
 /**
  * This is an optimized serializer for Lists that can be efficiently
  * traversed by index (as opposed to others, such as {@link LinkedList}
- * that can not}.
+ * that cannot}.
  */
 @JacksonStdImpl
 public final class IndexedListSerializer
@@ -48,7 +48,7 @@
     
     @Override
     public boolean isEmpty(SerializerProvider prov, List<?> value) {
-        return (value == null) || value.isEmpty();
+        return value.isEmpty();
     }
 
     @Override
@@ -81,15 +81,15 @@
     }
     
     @Override
-    public void serializeContents(List<?> value, JsonGenerator jgen, SerializerProvider provider)
+    public void serializeContents(List<?> value, JsonGenerator g, SerializerProvider provider)
         throws IOException
     {
         if (_elementSerializer != null) {
-            serializeContentsUsing(value, jgen, provider, _elementSerializer);
+            serializeContentsUsing(value, g, provider, _elementSerializer);
             return;
         }
         if (_valueTypeSerializer != null) {
-            serializeTypedContents(value, jgen, provider);
+            serializeTypedContents(value, g, provider);
             return;
         }
         final int len = value.size();
@@ -102,7 +102,7 @@
             for (; i < len; ++i) {
                 Object elem = value.get(i);
                 if (elem == null) {
-                    provider.defaultSerializeNull(jgen);
+                    provider.defaultSerializeNull(g);
                 } else {
                     Class<?> cc = elem.getClass();
                     JsonSerializer<Object> serializer = serializers.serializerFor(cc);
@@ -116,7 +116,7 @@
                         }
                         serializers = _dynamicSerializers;
                     }
-                    serializer.serialize(elem, jgen, provider);
+                    serializer.serialize(elem, g, provider);
                 }
             }
         } catch (Exception e) {
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/IndexedStringListSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/IndexedStringListSerializer.java
index e95dbaf..fbe88a6 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/IndexedStringListSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/IndexedStringListSerializer.java
@@ -4,6 +4,7 @@
 import java.util.*;
 
 import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.type.WritableTypeId;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonArrayFormatVisitor;
@@ -37,14 +38,13 @@
     }
 
     public IndexedStringListSerializer(IndexedStringListSerializer src,
-            JsonSerializer<?> ser, Boolean unwrapSingle) {
-        super(src, ser, unwrapSingle);
+            Boolean unwrapSingle) {
+        super(src, unwrapSingle);
     }
 
     @Override
-    public JsonSerializer<?> _withResolved(BeanProperty prop,
-            JsonSerializer<?> ser, Boolean unwrapSingle) {
-        return new IndexedStringListSerializer(this, ser, unwrapSingle);
+    public JsonSerializer<?> _withResolved(BeanProperty prop, Boolean unwrapSingle) {
+        return new IndexedStringListSerializer(this, unwrapSingle);
     }
     
     @Override protected JsonNode contentSchema() { return createSchemaNode("string", true); }
@@ -61,7 +61,7 @@
      */
 
     @Override
-    public void serialize(List<String> value, JsonGenerator gen,
+    public void serialize(List<String> value, JsonGenerator g,
             SerializerProvider provider) throws IOException
     {
         final int len = value.size();
@@ -69,75 +69,38 @@
             if (((_unwrapSingle == null) &&
                     provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED))
                     || (_unwrapSingle == Boolean.TRUE)) {
-                _serializeUnwrapped(value, gen, provider);
+                serializeContents(value, g, provider, 1);
                 return;
             }
         }
-        
-        gen.writeStartArray(len);
-        if (_serializer == null) {
-            serializeContents(value, gen, provider, len);
-        } else {
-            serializeUsingCustom(value, gen, provider, len);
-        }
-        gen.writeEndArray();
+        g.writeStartArray(len);
+        serializeContents(value, g, provider, len);
+        g.writeEndArray();
     }
 
-    private final void _serializeUnwrapped(List<String> value, JsonGenerator gen,
-            SerializerProvider provider) throws IOException
-    {
-        if (_serializer == null) {
-            serializeContents(value, gen, provider, 1);
-        } else {
-            serializeUsingCustom(value, gen, provider, 1);
-        }
-    }
-    
     @Override
-    public void serializeWithType(List<String> value, JsonGenerator gen,
-            SerializerProvider provider,
-            TypeSerializer typeSer) throws IOException
+    public void serializeWithType(List<String> value, JsonGenerator g, SerializerProvider provider,
+            TypeSerializer typeSer)
+        throws IOException
     {
-        final int len = value.size();
-        typeSer.writeTypePrefixForArray(value, gen);
-        if (_serializer == null) {
-            serializeContents(value, gen, provider, len);
-        } else {
-            serializeUsingCustom(value, gen, provider, len);
-        }
-        typeSer.writeTypeSuffixForArray(value, gen);
+        WritableTypeId typeIdDef = typeSer.writeTypePrefix(g,
+                typeSer.typeId(value, JsonToken.START_ARRAY));
+        serializeContents(value, g, provider, value.size());
+        typeSer.writeTypeSuffix(g, typeIdDef);
     }
 
-    private final void serializeContents(List<String> value, JsonGenerator gen,
+    private final void serializeContents(List<String> value, JsonGenerator g,
             SerializerProvider provider, int len) throws IOException
     {
+        g.setCurrentValue(value);
         int i = 0;
         try {
             for (; i < len; ++i) {
                 String str = value.get(i);
                 if (str == null) {
-                    provider.defaultSerializeNull(gen);
+                    provider.defaultSerializeNull(g);
                 } else {
-                    gen.writeString(str);
-                }
-            }
-        } catch (Exception e) {
-            wrapAndThrow(provider, e, value, i);
-        }
-    }
-
-    private final void serializeUsingCustom(List<String> value, JsonGenerator gen,
-            SerializerProvider provider, int len) throws IOException
-    {
-        int i = 0;
-        try {
-            final JsonSerializer<String> ser = _serializer;
-            for (i = 0; i < len; ++i) {
-                String str = value.get(i);
-                if (str == null) {
-                    provider.defaultSerializeNull(gen);
-                } else {
-                    ser.serialize(str, gen, provider);
+                    g.writeString(str);
                 }
             }
         } catch (Exception e) {
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/IteratorSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/IteratorSerializer.java
index 818e3bd..cf756db 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/IteratorSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/IteratorSerializer.java
@@ -27,7 +27,7 @@
 
     @Override
     public boolean isEmpty(SerializerProvider prov, Iterator<?> value) {
-        return (value == null) || !value.hasNext();
+        return !value.hasNext();
     }
 
     @Override
@@ -52,6 +52,8 @@
     public final void serialize(Iterator<?> value, JsonGenerator gen,
             SerializerProvider provider) throws IOException
     {
+        // 02-Dec-2016, tatu: As per comments above, can't determine single element so...
+        /*
         if (((_unwrapSingle == null) &&
                 provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED))
                 || (_unwrapSingle == Boolean.TRUE)) {
@@ -60,43 +62,64 @@
                 return;
             }
         }
+        */
         gen.writeStartArray();
         serializeContents(value, gen, provider);
         gen.writeEndArray();
     }
     
     @Override
-    public void serializeContents(Iterator<?> value, JsonGenerator gen,
+    public void serializeContents(Iterator<?> value, JsonGenerator g,
             SerializerProvider provider) throws IOException
     {
-        if (value.hasNext()) {
-            final TypeSerializer typeSer = _valueTypeSerializer;
-            JsonSerializer<Object> prevSerializer = null;
-            Class<?> prevClass = null;
-            do {
-                Object elem = value.next();
-                if (elem == null) {
-                    provider.defaultSerializeNull(gen);
-                    continue;
-                }
-                JsonSerializer<Object> currSerializer = _elementSerializer;
-                if (currSerializer == null) {
-                    // Minor optimization to avoid most lookups:
-                    Class<?> cc = elem.getClass();
-                    if (cc == prevClass) {
-                        currSerializer = prevSerializer;
-                    } else {
-                        currSerializer = provider.findValueSerializer(cc, _property);
-                        prevSerializer = currSerializer;
-                        prevClass = cc;
-                    }
-                }
-                if (typeSer == null) {
-                    currSerializer.serialize(elem, gen, provider);
-                } else {
-                    currSerializer.serializeWithType(elem, gen, provider, typeSer);
-                }
-            } while (value.hasNext());
+        if (!value.hasNext()) {
+            return;
         }
+        JsonSerializer<Object> serializer = _elementSerializer;
+        if (serializer == null) {
+            _serializeDynamicContents(value, g, provider);
+            return;
+        }
+        final TypeSerializer typeSer = _valueTypeSerializer;
+        do {
+            Object elem = value.next();
+            if (elem == null) {
+                provider.defaultSerializeNull(g);
+            } else if (typeSer == null) {
+                serializer.serialize(elem, g, provider);
+            } else {
+                serializer.serializeWithType(elem, g, provider, typeSer);
+            }
+        } while (value.hasNext());
+    }
+    
+    protected void _serializeDynamicContents(Iterator<?> value, JsonGenerator g,
+            SerializerProvider provider) throws IOException
+    {
+        final TypeSerializer typeSer = _valueTypeSerializer;
+        PropertySerializerMap serializers = _dynamicSerializers;
+        do {
+            Object elem = value.next();
+            if (elem == null) {
+                provider.defaultSerializeNull(g);
+                continue;
+            }
+            Class<?> cc = elem.getClass();
+            JsonSerializer<Object> serializer = serializers.serializerFor(cc);
+            if (serializer == null) {
+                if (_elementType.hasGenericTypes()) {
+                    serializer = _findAndAddDynamic(serializers,
+                            provider.constructSpecializedType(_elementType, cc), provider);
+                } else {
+                    serializer = _findAndAddDynamic(serializers, cc, provider);
+                }
+                serializers = _dynamicSerializers;
+            }
+            if (typeSer == null) {
+                serializer.serialize(elem, g, provider);
+            } else {
+                serializer.serializeWithType(elem, g, provider, typeSer);
+            }
+        } while (value.hasNext());
     }
 }
\ No newline at end of file
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/MapEntrySerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/MapEntrySerializer.java
index 7deb535..0917cce 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/MapEntrySerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/MapEntrySerializer.java
@@ -4,14 +4,19 @@
 import java.util.Map;
 import java.util.Map.Entry;
 
-import com.fasterxml.jackson.core.JsonGenerationException;
+import com.fasterxml.jackson.annotation.JsonInclude;
+
 import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.core.type.WritableTypeId;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
 import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
 import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
 import com.fasterxml.jackson.databind.ser.ContainerSerializer;
 import com.fasterxml.jackson.databind.ser.ContextualSerializer;
+import com.fasterxml.jackson.databind.util.ArrayBuilders;
+import com.fasterxml.jackson.databind.util.BeanUtil;
 
 /**
  * @since 2.5
@@ -23,6 +28,11 @@
     implements ContextualSerializer
 {
     /**
+     * @since 2.9
+     */
+    public final static Object MARKER_FOR_EMPTY = JsonInclude.Include.NON_EMPTY;
+
+    /**
      * Map-valued property being serialized with this instance
      */
     protected final BeanProperty _property;
@@ -35,11 +45,17 @@
 
     protected final JavaType _entryType, _keyType, _valueType;
 
+    /*
+    /**********************************************************
+    /* Serializers used
+    /**********************************************************
+     */
+
     /**
      * Key serializer to use, if it can be statically determined
      */
     protected JsonSerializer<Object> _keySerializer;
-    
+
     /**
      * Value serializer to use, if it can be statically determined
      */
@@ -51,17 +67,43 @@
     protected final TypeSerializer _valueTypeSerializer;
 
     /**
-     * If value type can not be statically determined, mapping from
+     * If value type cannot be statically determined, mapping from
      * runtime value types to serializers are stored in this object.
      */
     protected PropertySerializerMap _dynamicValueSerializers;
 
     /*
     /**********************************************************
+    /* Config settings, filtering
+    /**********************************************************
+     */
+
+    /**
+     * Value that indicates suppression mechanism to use for <b>values contained</b>;
+     * either "filter" (of which <code>equals()</code> is called), or marker
+     * value of {@link #MARKER_FOR_EMPTY}, or null to indicate no filtering for
+     * non-null values.
+     * Note that inclusion value for Map instance itself is handled by caller (POJO
+     * property that refers to the Map value).
+     * 
+     * @since 2.5
+     */
+    protected final Object _suppressableValue;
+
+    /**
+     * Flag that indicates what to do with `null` values, distinct from
+     * handling of {@link #_suppressableValue}
+     *
+     * @since 2.9
+     */
+    protected final boolean _suppressNulls;
+
+    /*
+    /**********************************************************
     /* Construction, initialization
     /**********************************************************
      */
-    
+
     public MapEntrySerializer(JavaType type, JavaType keyType, JavaType valueType,
             boolean staticTyping, TypeSerializer vts,
             BeanProperty property)
@@ -74,13 +116,25 @@
         _valueTypeSerializer = vts;
         _property = property;
         _dynamicValueSerializers = PropertySerializerMap.emptyForProperties();
+        _suppressableValue = null;
+        _suppressNulls = false;
     }
 
-    @SuppressWarnings("unchecked")
+    @Deprecated // since 2.9
     protected MapEntrySerializer(MapEntrySerializer src, BeanProperty property,
             TypeSerializer vts,
             JsonSerializer<?> keySer, JsonSerializer<?> valueSer)
     {
+        this(src, property, vts, keySer, valueSer,
+                src._suppressableValue, src._suppressNulls);
+    }
+        
+    @SuppressWarnings("unchecked")
+    protected MapEntrySerializer(MapEntrySerializer src, BeanProperty property,
+            TypeSerializer vts,
+            JsonSerializer<?> keySer, JsonSerializer<?> valueSer,
+            Object suppressableValue, boolean suppressNulls)
+    {
         super(Map.class, false);
         _entryType = src._entryType;
         _keyType = src._keyType;
@@ -91,16 +145,37 @@
         _valueSerializer = (JsonSerializer<Object>) valueSer;
         _dynamicValueSerializers = src._dynamicValueSerializers;
         _property = src._property;
+        _suppressableValue = suppressableValue;
+        _suppressNulls = suppressNulls;
     }
 
     @Override
     public ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer vts) {
-        return new MapEntrySerializer(this, _property, vts, _keySerializer, _valueSerializer);
+        return new MapEntrySerializer(this, _property, vts, _keySerializer, _valueSerializer,
+                _suppressableValue, _suppressNulls);
     }
 
+    /**
+     * @since 2.9
+     */
     public MapEntrySerializer withResolved(BeanProperty property,
-            JsonSerializer<?> keySerializer, JsonSerializer<?> valueSerializer) {
-        return new MapEntrySerializer(this, property, _valueTypeSerializer, keySerializer, valueSerializer);
+            JsonSerializer<?> keySerializer, JsonSerializer<?> valueSerializer,
+            Object suppressableValue, boolean suppressNulls) {
+        return new MapEntrySerializer(this, property, _valueTypeSerializer,
+                keySerializer, valueSerializer, suppressableValue, suppressNulls);
+    }
+
+    /**
+     * @since 2.9
+     */
+    public MapEntrySerializer withContentInclusion(Object suppressableValue,
+            boolean suppressNulls) {
+        if ((_suppressableValue == suppressableValue)
+                && (_suppressNulls == suppressNulls)) {
+            return this;
+        }
+        return new MapEntrySerializer(this, _property, _valueTypeSerializer,
+                _keySerializer, _valueSerializer, suppressableValue, suppressNulls);
     }
 
     @Override
@@ -127,7 +202,7 @@
             ser = _valueSerializer;
         }
         // [databind#124]: May have a content converter
-        ser = findConvertingContentSerializer(provider, property, ser);
+        ser = findContextualConvertingSerializer(provider, property, ser);
         if (ser == null) {
             // 30-Sep-2012, tatu: One more thing -- if explicit content type is annotated,
             //   we can consider it a static case as well.
@@ -135,8 +210,6 @@
             if (_valueTypeIsStatic && !_valueType.isJavaLangObject()) {
                 ser = provider.findValueSerializer(_valueType, property);
             }
-        } else {
-            ser = provider.handleSecondaryContextualization(ser, property);
         }
         if (keySer == null) {
             keySer = _keySerializer;
@@ -146,8 +219,59 @@
         } else {
             keySer = provider.handleSecondaryContextualization(keySer, property);
         }
-        MapEntrySerializer mser = withResolved(property, keySer, ser);
-        // but note: no filtering, ignored entries or sorting (unlike Maps)
+
+        Object valueToSuppress = _suppressableValue;
+        boolean suppressNulls = _suppressNulls;
+        if (property != null) {
+            JsonInclude.Value inclV = property.findPropertyInclusion(provider.getConfig(), null);
+            if (inclV != null) {
+                JsonInclude.Include incl = inclV.getContentInclusion();
+                if (incl != JsonInclude.Include.USE_DEFAULTS) {
+                    switch (incl) {
+                    case NON_DEFAULT:
+                        valueToSuppress = BeanUtil.getDefaultValue(_valueType);
+                        suppressNulls = true;
+                        if (valueToSuppress != null) {
+                            if (valueToSuppress.getClass().isArray()) {
+                                valueToSuppress = ArrayBuilders.getArrayComparator(valueToSuppress);
+                            }
+                        }
+                        break;
+                    case NON_ABSENT:
+                        suppressNulls = true;
+                        valueToSuppress = _valueType.isReferenceType() ? MARKER_FOR_EMPTY : null;
+                        break;
+                    case NON_EMPTY:
+                        suppressNulls = true;
+                        valueToSuppress = MARKER_FOR_EMPTY;
+                        break;
+                    case CUSTOM:
+                        valueToSuppress = provider.includeFilterInstance(null, inclV.getContentFilter());
+                        if (valueToSuppress == null) { // is this legal?
+                            suppressNulls = true;
+                        } else {
+                            suppressNulls = provider.includeFilterSuppressNulls(valueToSuppress);
+                        }
+                        break;
+                    case NON_NULL:
+                        valueToSuppress = null;
+                        suppressNulls = true;
+                        break;
+                    case ALWAYS: // default
+                    default:
+                        valueToSuppress = null;
+                        // 30-Sep-2016, tatu: Should not need to check global flags here,
+                        //   if inclusion forced to be ALWAYS
+                        suppressNulls = false;
+                        break;
+                    }
+                }
+            }
+        }
+        
+        MapEntrySerializer mser = withResolved(property, keySer, ser,
+                valueToSuppress, suppressNulls);
+        // but note: no (full) filtering or sorting (unlike Maps)
         return mser;
     }
 
@@ -173,8 +297,33 @@
     }
 
     @Override
-    public boolean isEmpty(SerializerProvider prov, Entry<?, ?> value) {
-        return (value == null);
+    public boolean isEmpty(SerializerProvider prov, Entry<?, ?> entry)
+    {
+        Object value = entry.getValue();
+        if (value == null) {
+            return _suppressNulls;
+        }
+        if (_suppressableValue == null) {
+            return false;
+        }
+        JsonSerializer<Object> valueSer = _valueSerializer;
+        if (valueSer == null) {
+            // Let's not worry about generic types here, actually;
+            // unlikely to make any difference, but does add significant overhead
+            Class<?> cc = value.getClass();
+            valueSer = _dynamicValueSerializers.serializerFor(cc);
+            if (valueSer == null) {
+                try {
+                    valueSer = _findAndAddDynamic(_dynamicValueSerializers, cc, prov);
+                } catch (JsonMappingException e) { // Ugh... cannot just throw as-is, so...
+                    return false;
+                }
+            }
+        }
+        if (_suppressableValue == MARKER_FOR_EMPTY) {
+            return valueSer.isEmpty(prov, value);
+        }
+        return _suppressableValue.equals(value);
     }
 
     /*
@@ -188,112 +337,79 @@
         throws IOException
     {
         gen.writeStartObject(value);
-        if (_valueSerializer != null) {
-            serializeUsing(value, gen, provider, _valueSerializer);
-        } else {
-            serializeDynamic(value, gen, provider);
-        }
+        serializeDynamic(value, gen, provider);
         gen.writeEndObject();
     }
 
     @Override
-    public void serializeWithType(Map.Entry<?, ?> value, JsonGenerator gen, SerializerProvider provider,
-            TypeSerializer typeSer) throws IOException
+    public void serializeWithType(Map.Entry<?, ?> value, JsonGenerator g,
+            SerializerProvider provider, TypeSerializer typeSer) throws IOException
     {
-        typeSer.writeTypePrefixForObject(value, gen);
         // [databind#631]: Assign current value, to be accessible by custom serializers
-        gen.setCurrentValue(value);
-        if (_valueSerializer != null) {
-            serializeUsing(value, gen, provider, _valueSerializer);
-        } else {
-            serializeDynamic(value, gen, provider);
-        }
-        typeSer.writeTypeSuffixForObject(value, gen);
+        g.setCurrentValue(value);
+        WritableTypeId typeIdDef = typeSer.writeTypePrefix(g,
+                typeSer.typeId(value, JsonToken.START_OBJECT));
+        serializeDynamic(value, g, provider);
+        typeSer.writeTypeSuffix(g, typeIdDef);
     }
 
-    protected void serializeDynamic(Map.Entry<?, ?> value, JsonGenerator jgen, SerializerProvider provider)
+    protected void serializeDynamic(Map.Entry<?, ?> value, JsonGenerator gen,
+            SerializerProvider provider)
         throws IOException
     {
-        final JsonSerializer<Object> keySerializer = _keySerializer;
-        final boolean skipNulls = !provider.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES);
         final TypeSerializer vts = _valueTypeSerializer;
+        final Object keyElem = value.getKey();
 
-        PropertySerializerMap serializers = _dynamicValueSerializers;
-
-        Object valueElem = value.getValue();
-        Object keyElem = value.getKey();
+        JsonSerializer<Object> keySerializer;
         if (keyElem == null) {
-            provider.findNullKeySerializer(_keyType, _property).serialize(null, jgen, provider);
+            keySerializer = provider.findNullKeySerializer(_keyType, _property);
         } else {
-            // [JACKSON-314] skip entries with null values?
-            if (skipNulls && valueElem == null) return;
-            keySerializer.serialize(keyElem, jgen, provider);
+            keySerializer = _keySerializer;
         }
+        // or by value; nulls often suppressed
+        final Object valueElem = value.getValue();
+        JsonSerializer<Object> valueSer;
         // And then value
         if (valueElem == null) {
-            provider.defaultSerializeNull(jgen);
-        } else {
-            Class<?> cc = valueElem.getClass();
-            JsonSerializer<Object> ser = serializers.serializerFor(cc);
-            if (ser == null) {
-                if (_valueType.hasGenericTypes()) {
-                    ser = _findAndAddDynamic(serializers,
-                            provider.constructSpecializedType(_valueType, cc), provider);
-                } else {
-                    ser = _findAndAddDynamic(serializers, cc, provider);
-                }
-                serializers = _dynamicValueSerializers;
+            if (_suppressNulls) {
+                return;
             }
-            try {
-                if (vts == null) {
-                    ser.serialize(valueElem, jgen, provider);
-                } else {
-                    ser.serializeWithType(valueElem, jgen, provider, vts);
+            valueSer = provider.getDefaultNullValueSerializer();
+        } else {
+            valueSer = _valueSerializer;
+            if (valueSer == null) {
+                Class<?> cc = valueElem.getClass();
+                valueSer = _dynamicValueSerializers.serializerFor(cc);
+                if (valueSer == null) {
+                    if (_valueType.hasGenericTypes()) {
+                        valueSer = _findAndAddDynamic(_dynamicValueSerializers,
+                                provider.constructSpecializedType(_valueType, cc), provider);
+                    } else {
+                        valueSer = _findAndAddDynamic(_dynamicValueSerializers, cc, provider);
+                    }
                 }
-            } catch (Exception e) {
-                // [JACKSON-55] Need to add reference information
-                String keyDesc = ""+keyElem;
-                wrapAndThrow(provider, e, value, keyDesc);
+            }
+            // also may need to skip non-empty values:
+            if (_suppressableValue != null) {
+                if (_suppressableValue == MARKER_FOR_EMPTY) {
+                    if (valueSer.isEmpty(provider, valueElem)) {
+                        return;
+                    }
+                } if (_suppressableValue.equals(valueElem)) {
+                    return;
+                }
             }
         }
-    }
-
-    /**
-     * Method called to serialize fields, when the value type is statically known,
-     * so that value serializer is passed and does not need to be fetched from
-     * provider.
-     */
-    protected void serializeUsing(Map.Entry<?, ?> value, JsonGenerator jgen, SerializerProvider provider,
-            JsonSerializer<Object> ser)
-        throws IOException, JsonGenerationException
-    {
-        final JsonSerializer<Object> keySerializer = _keySerializer;
-        final TypeSerializer vts = _valueTypeSerializer;
-        final boolean skipNulls = !provider.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES);
-
-        Object valueElem = value.getValue();
-        Object keyElem = value.getKey();
-        if (keyElem == null) {
-            provider.findNullKeySerializer(_keyType, _property).serialize(null, jgen, provider);
-        } else {
-            // [JACKSON-314] also may need to skip entries with null values
-            if (skipNulls && valueElem == null) return;
-            keySerializer.serialize(keyElem, jgen, provider);
-        }
-        if (valueElem == null) {
-            provider.defaultSerializeNull(jgen);
-        } else {
-            try {
-                if (vts == null) {
-                    ser.serialize(valueElem, jgen, provider);
-                } else {
-                    ser.serializeWithType(valueElem, jgen, provider, vts);
-                }
-            } catch (Exception e) {
-                // [JACKSON-55] Need to add reference information
-                String keyDesc = ""+keyElem;
-                wrapAndThrow(provider, e, value, keyDesc);
+        keySerializer.serialize(keyElem, gen, provider);
+        try {
+            if (vts == null) {
+                valueSer.serialize(valueElem, gen, provider);
+            } else {
+                valueSer.serializeWithType(valueElem, gen, provider, vts);
             }
+        } catch (Exception e) {
+            String keyDesc = ""+keyElem;
+            wrapAndThrow(provider, e, value, keyDesc);
         }
     }
 
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/ObjectIdWriter.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/ObjectIdWriter.java
index 42b4972..e532ad0 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/ObjectIdWriter.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/ObjectIdWriter.java
@@ -68,14 +68,7 @@
             ObjectIdGenerator<?> generator, boolean alwaysAsId)
     {
         String simpleName = (propName == null) ? null : propName.getSimpleName();
-        return construct(idType, simpleName, generator, alwaysAsId);
-    }
-    
-    @Deprecated // since 2.3
-    public static ObjectIdWriter construct(JavaType idType, String propName,
-            ObjectIdGenerator<?> generator, boolean alwaysAsId)
-    {
-        SerializableString serName = (propName == null) ? null : new SerializedString(propName);
+        SerializableString serName = (simpleName == null) ? null : new SerializedString(simpleName);
         return new ObjectIdWriter(idType, serName, generator, null, alwaysAsId);
     }
 
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/PropertySerializerMap.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/PropertySerializerMap.java
index b44ad4b..14f1802 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/PropertySerializerMap.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/PropertySerializerMap.java
@@ -42,7 +42,7 @@
 
     /**
      * Main lookup method. Takes a "raw" type since usage is always from
-     * place where parameterization is fixed such that there can not be
+     * place where parameterization is fixed such that there cannot be
      * type-parametric variations.
      */
     public abstract JsonSerializer<Object> serializerFor(Class<?> type);
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/StringArraySerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/StringArraySerializer.java
index bbd9e23..1011bab 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/StringArraySerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/StringArraySerializer.java
@@ -107,11 +107,9 @@
             ser = _elementSerializer;
         }
         // May have a content converter
-        ser = findConvertingContentSerializer(provider, property, ser);
+        ser = findContextualConvertingSerializer(provider, property, ser);
         if (ser == null) {
             ser = provider.findValueSerializer(String.class, property);
-        } else {
-            ser = provider.handleSecondaryContextualization(ser, property);
         }
         // Optimization: default serializer just writes String, so we can avoid a call:
         if (isDefaultSerializer(ser)) {
@@ -142,7 +140,7 @@
     
     @Override
     public boolean isEmpty(SerializerProvider prov, String[] value) {
-        return (value == null) || (value.length == 0);
+        return (value.length == 0);
     }
 
     @Override
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/StringCollectionSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/StringCollectionSerializer.java
index b45d8e9..b936eac 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/StringCollectionSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/StringCollectionSerializer.java
@@ -4,6 +4,7 @@
 import java.util.*;
 
 import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.type.WritableTypeId;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonArrayFormatVisitor;
@@ -36,17 +37,16 @@
     }
 
     protected StringCollectionSerializer(StringCollectionSerializer src,
-            JsonSerializer<?> ser, Boolean unwrapSingle)
+            Boolean unwrapSingle)
     {
-        super(src, ser, unwrapSingle);
+        super(src, unwrapSingle);
     }        
 
     @Override
-    public JsonSerializer<?> _withResolved(BeanProperty prop,
-            JsonSerializer<?> ser, Boolean unwrapSingle) {
-        return new StringCollectionSerializer(this, ser, unwrapSingle);
+    public JsonSerializer<?> _withResolved(BeanProperty prop, Boolean unwrapSingle) {
+        return new StringCollectionSerializer(this, unwrapSingle);
     }
-    
+
     @Override protected JsonNode contentSchema() {
         return createSchemaNode("string", true);
     }
@@ -64,88 +64,53 @@
      */
     
     @Override
-    public void serialize(Collection<String> value, JsonGenerator gen,
+    public void serialize(Collection<String> value, JsonGenerator g,
             SerializerProvider provider) throws IOException
     {
+        g.setCurrentValue(value);
         final int len = value.size();
         if (len == 1) {
             if (((_unwrapSingle == null) &&
                     provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED))
                     || (_unwrapSingle == Boolean.TRUE)) {
-                _serializeUnwrapped(value, gen, provider);
+                serializeContents(value, g, provider);
                 return;
             }
-        }      
-        gen.writeStartArray(len);
-        if (_serializer == null) {
-            serializeContents(value, gen, provider);
-        } else {
-            serializeUsingCustom(value, gen, provider);
         }
-        gen.writeEndArray();
-    }
-
-    private final void _serializeUnwrapped(Collection<String> value, JsonGenerator gen,
-            SerializerProvider provider) throws IOException
-    {
-        if (_serializer == null) {
-            serializeContents(value, gen, provider);
-        } else {
-            serializeUsingCustom(value, gen, provider);
-        }
+        g.writeStartArray(len);
+        serializeContents(value, g, provider);
+        g.writeEndArray();
     }
 
     @Override
-    public void serializeWithType(Collection<String> value, JsonGenerator jgen, SerializerProvider provider,
-            TypeSerializer typeSer)
-        throws IOException, JsonGenerationException
+    public void serializeWithType(Collection<String> value, JsonGenerator g,
+            SerializerProvider provider, TypeSerializer typeSer)
+        throws IOException
     {
-        typeSer.writeTypePrefixForArray(value, jgen);
-        if (_serializer == null) {
-            serializeContents(value, jgen, provider);
-        } else {
-            serializeUsingCustom(value, jgen, provider);
-        }
-        typeSer.writeTypeSuffixForArray(value, jgen);
+        g.setCurrentValue(value);
+        WritableTypeId typeIdDef = typeSer.writeTypePrefix(g,
+                typeSer.typeId(value, JsonToken.START_ARRAY));
+        serializeContents(value, g, provider);
+        typeSer.writeTypeSuffix(g, typeIdDef);
     }
 
-    private final void serializeContents(Collection<String> value, JsonGenerator jgen, SerializerProvider provider)
-        throws IOException, JsonGenerationException
+    private final void serializeContents(Collection<String> value, JsonGenerator g,
+            SerializerProvider provider)
+        throws IOException
     {
-        if (_serializer != null) {
-            serializeUsingCustom(value, jgen, provider);
-            return;
-        }
         int i = 0;
-        for (String str : value) {
-            try {
+
+        try {
+            for (String str : value) {
                 if (str == null) {
-                    provider.defaultSerializeNull(jgen);
+                    provider.defaultSerializeNull(g);
                 } else {
-                    jgen.writeString(str);
+                    g.writeString(str);
                 }
                 ++i;
-            } catch (Exception e) {
-                wrapAndThrow(provider, e, value, i);
             }
+        } catch (Exception e) {
+            wrapAndThrow(provider, e, value, i);
         }
     }
-
-    private void serializeUsingCustom(Collection<String> value, JsonGenerator jgen, SerializerProvider provider)
-        throws IOException, JsonGenerationException
-    {
-        final JsonSerializer<String> ser = _serializer;
-        int i = 0;
-        for (String str : value) {
-            try {
-                if (str == null) {
-                    provider.defaultSerializeNull(jgen);
-                } else {
-                    ser.serialize(str, jgen, provider);
-                }
-            } catch (Exception e) {
-                wrapAndThrow(provider, e, value, i);
-            }
-       }
-    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/TypeWrappedSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/TypeWrappedSerializer.java
index e3837a6..62ad61d 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/TypeWrappedSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/TypeWrappedSerializer.java
@@ -3,10 +3,9 @@
 import java.io.IOException;
 
 import com.fasterxml.jackson.core.JsonGenerator;
-
-import com.fasterxml.jackson.databind.JsonSerializer;
-import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+import com.fasterxml.jackson.databind.ser.ContextualSerializer;
 
 /**
  * Simple serializer that will call configured type serializer, passing
@@ -15,6 +14,7 @@
  */
 public final class TypeWrappedSerializer
     extends JsonSerializer<Object>
+    implements ContextualSerializer // since 2.9
 {
     final protected TypeSerializer _typeSerializer;
     final protected JsonSerializer<Object> _serializer;
@@ -28,25 +28,45 @@
     }
 
     @Override
-    public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
-        _serializer.serializeWithType(value, jgen, provider, _typeSerializer);
+    public void serialize(Object value, JsonGenerator g, SerializerProvider provider) throws IOException {
+        _serializer.serializeWithType(value, g, provider, _typeSerializer);
     }
 
     @Override
-    public void serializeWithType(Object value, JsonGenerator jgen, SerializerProvider provider,
+    public void serializeWithType(Object value, JsonGenerator g, SerializerProvider provider,
             TypeSerializer typeSer) throws IOException
     {
-        /* Is this an erroneous call? For now, let's assume it is not, and
-         * that type serializer is just overridden if so
-         */
-        _serializer.serializeWithType(value, jgen, provider, typeSer);
+        // Is this an erroneous call? For now, let's assume it is not, and
+        // that type serializer is just overridden if so
+        _serializer.serializeWithType(value, g, provider, typeSer);
     }
-    
+
     @Override
     public Class<Object> handledType() { return Object.class; }
 
     /*
     /**********************************************************
+    /* ContextualDeserializer
+    /**********************************************************
+     */
+
+    @Override // since 2.9
+    public JsonSerializer<?> createContextual(SerializerProvider provider, BeanProperty property)
+        throws JsonMappingException
+    {
+        // 13-Mar-2017, tatu: Should we call `TypeSerializer.forProperty()`?
+        JsonSerializer<?> ser = _serializer;
+        if (ser instanceof ContextualSerializer) {
+            ser = provider.handleSecondaryContextualization(ser, property);
+        }
+        if (ser == _serializer) {
+            return this;
+        }
+        return new TypeWrappedSerializer(_typeSerializer, ser);
+    }
+
+    /*
+    /**********************************************************
     /* Extended API for other core classes
     /**********************************************************
      */
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnknownSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnknownSerializer.java
index 6c1530b..70b53e0 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnknownSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnknownSerializer.java
@@ -4,6 +4,7 @@
 import java.lang.reflect.Type;
 
 import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.type.WritableTypeId;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
 import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
@@ -43,8 +44,9 @@
         if (provider.isEnabled(SerializationFeature.FAIL_ON_EMPTY_BEANS)) {
             failForEmpty(provider, value);
         }
-        typeSer.writeTypePrefixForObject(value, gen);
-        typeSer.writeTypeSuffixForObject(value, gen);
+        WritableTypeId typeIdDef = typeSer.writeTypePrefix(gen,
+                typeSer.typeId(value, JsonToken.START_OBJECT));
+        typeSer.writeTypeSuffix(gen, typeIdDef);
     }
 
     @Override
@@ -66,7 +68,8 @@
 
     protected void failForEmpty(SerializerProvider prov, Object value)
             throws JsonMappingException {
-        prov.reportMappingProblem("No serializer found for class %s and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)",
-                value.getClass().getName());
+        prov.reportBadDefinition(handledType(), String.format(
+                "No serializer found for class %s and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)",
+                value.getClass().getName()));
     }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnwrappingBeanPropertyWriter.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnwrappingBeanPropertyWriter.java
index 9b558ce..c6498b0 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnwrappingBeanPropertyWriter.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnwrappingBeanPropertyWriter.java
@@ -134,14 +134,17 @@
     @Override
     public void assignSerializer(JsonSerializer<Object> ser)
     {
-        super.assignSerializer(ser);
-        if (_serializer != null) {
+        if (ser != null) {
             NameTransformer t = _nameTransformer;
-            if (_serializer.isUnwrappingSerializer()) {
-                t = NameTransformer.chainedTransformer(t, ((UnwrappingBeanSerializer) _serializer)._nameTransformer);
+            if (ser.isUnwrappingSerializer()
+                    // as per [databind#2060], need to also check this, in case someone writes
+                    // custom implementation that does not extend standard implementation:
+                    && (ser instanceof UnwrappingBeanSerializer)) {
+                t = NameTransformer.chainedTransformer(t, ((UnwrappingBeanSerializer) ser)._nameTransformer);
             }
-            _serializer = _serializer.unwrappingSerializer(t);
+            ser = ser.unwrappingSerializer(t);
         }
+        super.assignSerializer(ser);
     }
 
     /*
@@ -210,8 +213,11 @@
             serializer = provider.findValueSerializer(type, this);
         }
         NameTransformer t = _nameTransformer;
-        if (serializer.isUnwrappingSerializer()) {
-            t = NameTransformer.chainedTransformer(t, ((UnwrappingBeanSerializer) serializer)._nameTransformer);
+        if (serializer.isUnwrappingSerializer()
+            // as per [databind#2060], need to also check this, in case someone writes
+            // custom implementation that does not extend standard implementation:
+            && (serializer instanceof UnwrappingBeanSerializer)) {
+                t = NameTransformer.chainedTransformer(t, ((UnwrappingBeanSerializer) serializer)._nameTransformer);
         }
         serializer = serializer.unwrappingSerializer(t);
         
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnwrappingBeanSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnwrappingBeanSerializer.java
index 834e03a..3b91926 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnwrappingBeanSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnwrappingBeanSerializer.java
@@ -87,7 +87,7 @@
     }
 
     /**
-     * JSON Array output can not be done if unwrapping operation is
+     * JSON Array output cannot be done if unwrapping operation is
      * requested; so implementation will simply return 'this'.
      */
     @Override
@@ -126,7 +126,8 @@
     		TypeSerializer typeSer) throws IOException
     {
         if (provider.isEnabled(SerializationFeature.FAIL_ON_UNWRAPPED_TYPE_IDENTIFIERS)) {
-            provider.reportMappingProblem("Unwrapped property requires use of type information: can not serialize without disabling `SerializationFeature.FAIL_ON_UNWRAPPED_TYPE_IDENTIFIERS`");
+            provider.reportBadDefinition(handledType(),
+                    "Unwrapped property requires use of type information: cannot serialize without disabling `SerializationFeature.FAIL_ON_UNWRAPPED_TYPE_IDENTIFIERS`");
         }
         gen.setCurrentValue(bean); // [databind#631]
         if (_objectIdWriter != null) {
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/ArraySerializerBase.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/ArraySerializerBase.java
index c89409c..69af378 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/ArraySerializerBase.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/ArraySerializerBase.java
@@ -4,6 +4,7 @@
 
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.type.WritableTypeId;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
 import com.fasterxml.jackson.databind.ser.*;
@@ -29,7 +30,7 @@
      * @since 2.6
      */
     protected final Boolean _unwrapSingle;
-    
+
     protected ArraySerializerBase(Class<T> cls)
     {
         super(cls);
@@ -111,9 +112,7 @@
     @Override
     public void serialize(T value, JsonGenerator gen, SerializerProvider provider) throws IOException
     {
-        if (((_unwrapSingle == null) &&
-                provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED))
-                || (_unwrapSingle == Boolean.TRUE)) {
+        if (_shouldUnwrapSingle(provider)) {
             if (hasSingleElement(value)) {
                 serializeContents(value, gen, provider);
                 return;
@@ -127,17 +126,28 @@
     }
 
     @Override
-    public final void serializeWithType(T value, JsonGenerator gen, SerializerProvider provider,
+    public final void serializeWithType(T value, JsonGenerator g, SerializerProvider provider,
             TypeSerializer typeSer)
         throws IOException
     {
-        typeSer.writeTypePrefixForArray(value, gen);
         // [databind#631]: Assign current value, to be accessible by custom serializers
-        gen.setCurrentValue(value);
-        serializeContents(value, gen, provider);
-        typeSer.writeTypeSuffixForArray(value, gen);
+        g.setCurrentValue(value);
+        WritableTypeId typeIdDef = typeSer.writeTypePrefix(g,
+                typeSer.typeId(value, JsonToken.START_ARRAY));
+        serializeContents(value, g, provider);
+        typeSer.writeTypeSuffix(g, typeIdDef);
     }
-    
+
     protected abstract void serializeContents(T value, JsonGenerator jgen, SerializerProvider provider)
         throws IOException;
+
+    /**
+     * @since 2.9
+     */
+    protected final boolean _shouldUnwrapSingle(SerializerProvider provider) {
+        if (_unwrapSingle == null) {
+            return provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED);
+        }
+        return _unwrapSingle.booleanValue();
+    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/AsArraySerializerBase.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/AsArraySerializerBase.java
index a1bd6b9..ba1665a 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/AsArraySerializerBase.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/AsArraySerializerBase.java
@@ -6,7 +6,7 @@
 import com.fasterxml.jackson.annotation.JsonFormat;
 
 import com.fasterxml.jackson.core.*;
-
+import com.fasterxml.jackson.core.type.WritableTypeId;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
@@ -56,7 +56,7 @@
     protected final JsonSerializer<Object> _elementSerializer;
 
     /**
-     * If element type can not be statically determined, mapping from
+     * If element type cannot be statically determined, mapping from
      * runtime type to serializer is handled using this object
      */
     protected PropertySerializerMap _dynamicSerializers;
@@ -191,7 +191,7 @@
             ser = _elementSerializer;
         }
         // 18-Feb-2013, tatu: May have a content converter:
-        ser = findConvertingContentSerializer(serializers, property, ser);
+        ser = findContextualConvertingSerializer(serializers, property, ser);
         if (ser == null) {
             // 30-Sep-2012, tatu: One more thing -- if explicit content type is annotated,
             //   we can consider it a static case as well.
@@ -200,8 +200,6 @@
                     ser = serializers.findValueSerializer(_elementType, property);
                 }
             }
-        } else {
-            ser = serializers.handleSecondaryContextualization(ser, property);
         }
         if ((ser != _elementSerializer)
                 || (property != _property)
@@ -253,15 +251,15 @@
     }
 
     @Override
-    public void serializeWithType(T value, JsonGenerator gen, SerializerProvider provider,
+    public void serializeWithType(T value, JsonGenerator g, SerializerProvider provider,
             TypeSerializer typeSer) throws IOException
     {
-        // note: let's NOT consider [JACKSON-805] here; gets too complicated, and probably just won't work
-        typeSer.writeTypePrefixForArray(value, gen);
         // [databind#631]: Assign current value, to be accessible by custom serializers
-        gen.setCurrentValue(value);
-        serializeContents(value, gen, provider);
-        typeSer.writeTypeSuffixForArray(value, gen);
+        g.setCurrentValue(value);
+        WritableTypeId typeIdDef = typeSer.writeTypePrefix(g,
+                typeSer.typeId(value, JsonToken.START_ARRAY));
+        serializeContents(value, g, provider);
+        typeSer.writeTypeSuffix(g, typeIdDef);
     }
 
     protected abstract void serializeContents(T value, JsonGenerator gen, SerializerProvider provider)
@@ -273,15 +271,10 @@
         throws JsonMappingException
     {
         ObjectNode o = createSchemaNode("array", true);
-        JavaType contentType = _elementType;
-        if (contentType != null) {
+        if (_elementSerializer != null) {
             JsonNode schemaNode = null;
-            // 15-Oct-2010, tatu: We can't serialize plain Object.class; but what should it produce here? Untyped?
-            if (contentType.getRawClass() != Object.class) {
-                JsonSerializer<Object> ser = provider.findValueSerializer(contentType, _property);
-                if (ser instanceof SchemaAware) {
-                    schemaNode = ((SchemaAware) ser).getSchema(provider, null);
-                }
+            if (_elementSerializer instanceof SchemaAware) {
+                schemaNode = ((SchemaAware) _elementSerializer).getSchema(provider, null);
             }
             if (schemaNode == null) {
                 schemaNode = com.fasterxml.jackson.databind.jsonschema.JsonSchema.getDefaultSchemaNode();
@@ -297,7 +290,11 @@
     {
         JsonSerializer<?> valueSer = _elementSerializer;
         if (valueSer == null) {
-            valueSer = visitor.getProvider().findValueSerializer(_elementType, _property);
+            // 19-Oct-2016, tatu: Apparently we get null for untyped/raw `EnumSet`s... not 100%
+            //   sure what'd be the clean way but let's try this for now:
+            if (_elementType != null) {
+                valueSer = visitor.getProvider().findValueSerializer(_elementType, _property);
+            }
         }
         visitArrayFormat(visitor, typeHint, valueSer, _elementType);
     }
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/AtomicReferenceSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/AtomicReferenceSerializer.java
index 7726bb0..d44d9af 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/AtomicReferenceSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/AtomicReferenceSerializer.java
@@ -2,8 +2,6 @@
 
 import java.util.concurrent.atomic.AtomicReference;
 
-import com.fasterxml.jackson.annotation.JsonInclude;
-
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
 import com.fasterxml.jackson.databind.type.ReferenceType;
@@ -29,23 +27,28 @@
     protected AtomicReferenceSerializer(AtomicReferenceSerializer base, BeanProperty property,
             TypeSerializer vts, JsonSerializer<?> valueSer,
             NameTransformer unwrapper,
-            JsonInclude.Include contentIncl)
+            Object suppressableValue, boolean suppressNulls)
     {
-        super(base, property, vts, valueSer, unwrapper, contentIncl);
+        super(base, property, vts, valueSer, unwrapper,
+                suppressableValue, suppressNulls);
     }
 
     @Override
-    protected AtomicReferenceSerializer withResolved(BeanProperty prop,
+    protected ReferenceTypeSerializer<AtomicReference<?>> withResolved(BeanProperty prop,
             TypeSerializer vts, JsonSerializer<?> valueSer,
-            NameTransformer unwrapper,
-            JsonInclude.Include contentIncl)
+            NameTransformer unwrapper)
     {
-        if ((_property == prop) && (contentIncl == _contentInclusion)
-                && (_valueTypeSerializer == vts) && (_valueSerializer == valueSer)
-                && (_unwrapper == unwrapper)) {
-            return this;
-        }
-        return new AtomicReferenceSerializer(this, prop, vts, valueSer, unwrapper, contentIncl);
+        return new AtomicReferenceSerializer(this, prop, vts, valueSer, unwrapper,
+                _suppressableValue, _suppressNulls);
+    }
+
+    @Override
+    public ReferenceTypeSerializer<AtomicReference<?>> withContentInclusion(Object suppressableValue,
+            boolean suppressNulls)
+    {
+        return new AtomicReferenceSerializer(this, _property, _valueTypeSerializer,
+                _valueSerializer, _unwrapper,
+                suppressableValue, suppressNulls);
     }
 
     /*
@@ -55,8 +58,8 @@
      */
 
     @Override
-    protected boolean _isValueEmpty(AtomicReference<?> value) {
-        return value.get() == null;
+    protected boolean _isValuePresent(AtomicReference<?> value) {
+        return value.get() != null;
     }
 
     @Override
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/BeanSerializerBase.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/BeanSerializerBase.java
index ab0850e..a95d812 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/BeanSerializerBase.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/BeanSerializerBase.java
@@ -6,6 +6,7 @@
 
 import com.fasterxml.jackson.annotation.*;
 import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.type.WritableTypeId;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
 import com.fasterxml.jackson.databind.introspect.ObjectIdInfo;
@@ -17,6 +18,7 @@
 import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.fasterxml.jackson.databind.ser.*;
+import com.fasterxml.jackson.databind.ser.impl.MapEntrySerializer;
 import com.fasterxml.jackson.databind.ser.impl.ObjectIdWriter;
 import com.fasterxml.jackson.databind.ser.impl.PropertyBasedObjectIdGenerator;
 import com.fasterxml.jackson.databind.ser.impl.WritableObjectId;
@@ -37,7 +39,7 @@
         JsonFormatVisitable, SchemaAware
 {
     protected final static PropertyName NAME_FOR_OBJECT_REF = new PropertyName("#object-ref");
-    
+
     final protected static BeanPropertyWriter[] NO_PROPS = new BeanPropertyWriter[0];
 
     /*
@@ -47,6 +49,11 @@
      */
 
     /**
+     * @since 2.9
+     */
+    final protected JavaType _beanType;
+
+    /**
      * Writers used for outputting actual property values
      */
     final protected BeanPropertyWriter[] _props;
@@ -103,6 +110,7 @@
             BeanPropertyWriter[] properties, BeanPropertyWriter[] filteredProperties)
     {
         super(type);
+        _beanType = type;
         _props = properties;
         _filteredProps = filteredProperties;
         if (builder == null) { // mostly for testing
@@ -125,6 +133,7 @@
             BeanPropertyWriter[] properties, BeanPropertyWriter[] filteredProperties)
     {
         super(src._handledType);
+        _beanType = src._beanType;
         _props = properties;
         _filteredProps = filteredProperties;
 
@@ -148,6 +157,7 @@
             ObjectIdWriter objectIdWriter, Object filterId)
     {
         super(src._handledType);
+        _beanType = src._beanType;
         _props = src._props;
         _filteredProps = src._filteredProps;
         
@@ -168,6 +178,7 @@
     {
         super(src._handledType);
 
+        _beanType = src._beanType;
         final BeanPropertyWriter[] propsIn = src._props;
         final BeanPropertyWriter[] fpropsIn = src._filteredProps;
         final int len = propsIn.length;
@@ -318,9 +329,6 @@
                 // It not, we can use declared return type if and only if declared type is final:
                 // if not, we don't really know the actual type until we get the instance.
                 if (type == null) {
-                    // 30-Oct-2015, tatu: Not sure why this was used
-//                    type = provider.constructType(prop.getGenericPropertyType());
-                    // but this looks better
                     type = prop.getType();
                     if (!type.isFinal()) {
                         if (type.isContainerType() || type.containedTypeCount() > 0) {
@@ -346,14 +354,18 @@
                     }
                 }
             }
-            prop.assignSerializer(ser);
-            // and maybe replace filtered property too? (see [JACKSON-364])
+            // and maybe replace filtered property too?
             if (i < filteredCount) {
                 BeanPropertyWriter w2 = _filteredProps[i];
                 if (w2 != null) {
                     w2.assignSerializer(ser);
+                    // 17-Mar-2017, tatu: Typically will lead to chained call to original property,
+                    //    which would lead to double set. Not a problem itself, except... unwrapping
+                    //    may require work to be done, which does lead to an actual issue.
+                    continue;
                 }
             }
+            prop.assignSerializer(ser);
         }
 
         // also, any-getter may need to be resolved
@@ -418,11 +430,27 @@
                     case NUMBER_INT:
                         // 12-Oct-2014, tatu: May need to introspect full annotations... but
                         //   for now, just do class ones
-                        BeanDescription desc = config.introspectClassAnnotations(_handledType);
-                        JsonSerializer<?> ser = EnumSerializer.construct(_handledType,
+                        BeanDescription desc = config.introspectClassAnnotations(_beanType);
+                        JsonSerializer<?> ser = EnumSerializer.construct(_beanType.getRawClass(),
                                 provider.getConfig(), desc, format);
                         return provider.handlePrimaryContextualization(ser, property);
                     }
+                // 16-Oct-2016, tatu: Ditto for `Map`, `Map.Entry` subtypes
+                } else if (shape == JsonFormat.Shape.NATURAL) {
+                    if (_beanType.isMapLikeType() && Map.class.isAssignableFrom(_handledType)) {
+                        ;
+                    } else if (Map.Entry.class.isAssignableFrom(_handledType)) {
+                        JavaType mapEntryType = _beanType.findSuperType(Map.Entry.class);
+
+                        JavaType kt = mapEntryType.containedTypeOrUnknown(0);
+                        JavaType vt = mapEntryType.containedTypeOrUnknown(1);
+
+                        // 16-Oct-2016, tatu: could have problems with type handling, as we do not
+                        //   see if "static" typing is needed, nor look for `TypeSerializer` yet...
+                        JsonSerializer<?> ser = new MapEntrySerializer(_beanType, kt, vt,
+                                false, null, property);
+                        return provider.handlePrimaryContextualization(ser, property);
+                    }
                 }
             }
         }
@@ -461,17 +489,17 @@
                     String propName = objectIdInfo.getPropertyName().getSimpleName();
                     BeanPropertyWriter idProp = null;
 
-                    for (int i = 0, len = _props.length ;; ++i) {
+                    for (int i = 0, len = _props.length; ; ++i) {
                         if (i == len) {
-                            throw new IllegalArgumentException("Invalid Object Id definition for "+_handledType.getName()
-                                    +": can not find property with name '"+propName+"'");
+                            provider.reportBadDefinition(_beanType, String.format(
+                                    "Invalid Object Id definition for %s: cannot find property with name '%s'",
+                                    handledType().getName(), propName));
                         }
                         BeanPropertyWriter prop = _props[i];
                         if (propName.equals(prop.getName())) {
                             idProp = prop;
-                            /* Let's force it to be the first property to output
-                             * (although it may still get rearranged etc)
-                             */
+                            // Let's force it to be the first property to output
+                            // (although it may still get rearranged etc)
                             if (i > 0) { // note: must shuffle both regular properties and filtered
                                 System.arraycopy(_props, 0, _props, 1, i);
                                 _props[0] = idProp;
@@ -493,7 +521,6 @@
                             objectIdInfo.getAlwaysAsId());
                 }
             }
-            
             // Or change Filter Id in use?
             Object filterId = intr.findFilterId(accessor);
             if (filterId != null) {
@@ -522,6 +549,7 @@
         if (shape == null) {
             shape = _serializationShape;
         }
+        // last but not least; may need to transmute into as-array serialization
         if (shape == JsonFormat.Shape.ARRAY) {
             return contextual.asArraySerializer();
         }
@@ -567,23 +595,15 @@
             return;
         }
 
-        String typeStr = (_typeId == null) ? null : _customTypeId(bean);
-        if (typeStr == null) {
-            typeSer.writeTypePrefixForObject(bean, gen);
-        } else {
-            typeSer.writeCustomTypePrefixForObject(bean, gen, typeStr);
-        }
         gen.setCurrentValue(bean); // [databind#631]
+        WritableTypeId typeIdDef = _typeIdDef(typeSer, bean, JsonToken.START_OBJECT);
+        typeSer.writeTypePrefix(gen, typeIdDef);
         if (_propertyFilterId != null) {
             serializeFieldsFiltered(bean, gen, provider);
         } else {
             serializeFields(bean, gen, provider);
         }
-        if (typeStr == null) {
-            typeSer.writeTypeSuffixForObject(bean, gen);
-        } else {
-            typeSer.writeCustomTypeSuffixForObject(bean, gen, typeStr);
-        }
+        typeSer.writeTypeSuffix(gen, typeIdDef);
     }
 
     protected final void _serializeWithObjectId(Object bean, JsonGenerator gen, SerializerProvider provider,
@@ -630,33 +650,43 @@
             w.serializer.serialize(id, gen, provider);
             return;
         }
-
         _serializeObjectId(bean, gen, provider, typeSer, objectId);
     }
 
-    protected  void _serializeObjectId(Object bean, JsonGenerator gen,SerializerProvider provider,
+    protected  void _serializeObjectId(Object bean, JsonGenerator g,
+            SerializerProvider provider,
             TypeSerializer typeSer, WritableObjectId objectId) throws IOException
     {
         final ObjectIdWriter w = _objectIdWriter;
-        String typeStr = (_typeId == null) ? null :_customTypeId(bean);
-        if (typeStr == null) {
-            typeSer.writeTypePrefixForObject(bean, gen);
-        } else {
-            typeSer.writeCustomTypePrefixForObject(bean, gen, typeStr);
-        }
-        objectId.writeAsField(gen, provider, w);
+        WritableTypeId typeIdDef = _typeIdDef(typeSer, bean, JsonToken.START_OBJECT);
+
+        typeSer.writeTypePrefix(g, typeIdDef);
+        objectId.writeAsField(g, provider, w);
         if (_propertyFilterId != null) {
-            serializeFieldsFiltered(bean, gen, provider);
+            serializeFieldsFiltered(bean, g, provider);
         } else {
-            serializeFields(bean, gen, provider);
+            serializeFields(bean, g, provider);
         }
-        if (typeStr == null) {
-            typeSer.writeTypeSuffixForObject(bean, gen);
-        } else {
-            typeSer.writeCustomTypeSuffixForObject(bean, gen, typeStr);
-        }
+        typeSer.writeTypeSuffix(g, typeIdDef);
     }
-    
+
+    /**
+     * @since 2.9
+     */
+    protected final WritableTypeId _typeIdDef(TypeSerializer typeSer,
+            Object bean, JsonToken valueShape) {
+        if (_typeId == null) {
+            return typeSer.typeId(bean, valueShape);
+        }
+        Object typeId = _typeId.getValue(bean);
+        if (typeId == null) {
+            // 28-Jun-2017, tatu: Is this really needed? Unchanged from 2.8 but...
+            typeId = "";
+        }
+        return typeSer.typeId(bean, valueShape, typeId);
+    }
+
+    @Deprecated // since 2.9
     protected final String _customTypeId(Object bean)
     {
         final Object typeId = _typeId.getValue(bean);
@@ -665,7 +695,7 @@
         }
         return (typeId instanceof String) ? (String) typeId : typeId.toString();
     }
-    
+
     /*
     /**********************************************************
     /* Field serialization methods
@@ -696,10 +726,9 @@
             String name = (i == props.length) ? "[anySetter]" : props[i].getName();
             wrapAndThrow(provider, e, bean, name);
         } catch (StackOverflowError e) {
-            /* 04-Sep-2009, tatu: Dealing with this is tricky, since we do not
-             *   have many stack frames to spare... just one or two; can't
-             *   make many calls.
-             */
+            // 04-Sep-2009, tatu: Dealing with this is tricky, since we don't have many
+            //   stack frames to spare... just one or two; can't make many calls.
+
             // 10-Dec-2015, tatu: and due to above, avoid "from" method, call ctor directly:
             //JsonMappingException mapE = JsonMappingException.from(gen, "Infinite recursion (StackOverflowError)", e);
             JsonMappingException mapE = new JsonMappingException(gen, "Infinite recursion (StackOverflowError)", e);
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/BooleanSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/BooleanSerializer.java
index a69a88c..135add0 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/BooleanSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/BooleanSerializer.java
@@ -3,25 +3,32 @@
 import java.io.IOException;
 import java.lang.reflect.Type;
 
+import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.core.JsonGenerator;
-
+import com.fasterxml.jackson.core.JsonParser.NumberType;
+import com.fasterxml.jackson.databind.BeanProperty;
 import com.fasterxml.jackson.databind.JavaType;
 import com.fasterxml.jackson.databind.JsonMappingException;
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.JsonSerializer;
 import com.fasterxml.jackson.databind.SerializerProvider;
 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+import com.fasterxml.jackson.databind.ser.ContextualSerializer;
 
 /**
  * Serializer used for primitive boolean, as well as java.util.Boolean
  * wrapper type.
  *<p>
- * Since this is one of "native" types, no type information is ever
- * included on serialization (unlike for most scalar types as of 1.5)
+ * Since this is one of "natural" (aka "native") types, no type information is ever
+ * included on serialization (unlike for most other scalar types)
  */
 @JacksonStdImpl
 public final class BooleanSerializer
-    extends NonTypedScalarSerializerBase<Boolean>
+//In 2.9, removed use of intermediate type `NonTypedScalarSerializerBase`
+    extends StdScalarSerializer<Object>
+    implements ContextualSerializer
 {
     private static final long serialVersionUID = 1L;
 
@@ -32,25 +39,106 @@
     protected final boolean _forPrimitive;
 
     public BooleanSerializer(boolean forPrimitive) {
-        super(Boolean.class);
+        super(forPrimitive ? Boolean.TYPE : Boolean.class, false);
         _forPrimitive = forPrimitive;
     }
 
     @Override
-    public void serialize(Boolean value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
-        jgen.writeBoolean(value.booleanValue());
+    public JsonSerializer<?> createContextual(SerializerProvider serializers,
+            BeanProperty property) throws JsonMappingException
+    {
+        JsonFormat.Value format = findFormatOverrides(serializers,
+                property, Boolean.class);
+        if (format != null) {
+            JsonFormat.Shape shape = format.getShape();
+            if (shape.isNumeric()) {
+                return new AsNumber(_forPrimitive);
+            }
+        }
+        return this;
+    }
+
+    @Override
+    public void serialize(Object value, JsonGenerator g, SerializerProvider provider) throws IOException {
+        g.writeBoolean(Boolean.TRUE.equals(value));
+    }
+
+    @Override
+    public final void serializeWithType(Object value, JsonGenerator g, SerializerProvider provider,
+            TypeSerializer typeSer) throws IOException
+    {
+        g.writeBoolean(Boolean.TRUE.equals(value));
     }
 
     @Override
     public JsonNode getSchema(SerializerProvider provider, Type typeHint) {
         return createSchemaNode("boolean", !_forPrimitive);
     }
-    
+
     @Override
-    public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException
+    public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException {
+        visitor.expectBooleanFormat(typeHint);
+    }
+
+    /**
+     * Alternate implementation that is used when values are to be serialized
+     * as numbers <code>0</code> (false) or <code>1</code> (true).
+     * 
+     * @since 2.9
+     */
+    final static class AsNumber
+        extends StdScalarSerializer<Object>
+        implements ContextualSerializer
     {
-        if (visitor != null) {
-            visitor.expectBooleanFormat(typeHint);
+        private static final long serialVersionUID = 1L;
+
+        /**
+         * Whether type serialized is primitive (boolean) or wrapper
+         * (java.lang.Boolean); if true, former, if false, latter.
+         */
+        protected final boolean _forPrimitive;
+
+        public AsNumber(boolean forPrimitive) {
+            super(forPrimitive ? Boolean.TYPE : Boolean.class, false);
+            _forPrimitive = forPrimitive;
+        }
+
+        @Override
+        public void serialize(Object value, JsonGenerator g, SerializerProvider provider) throws IOException {
+            g.writeNumber((Boolean.FALSE.equals(value)) ? 0 : 1);
+        }
+
+        @Override
+        public final void serializeWithType(Object value, JsonGenerator g, SerializerProvider provider,
+                TypeSerializer typeSer) throws IOException
+        {
+            // 27-Mar-2017, tatu: Actually here we CAN NOT serialize as number without type,
+            //    since with natural types that would map to number, not boolean. So choice
+            //    comes to between either add type id, or serialize as boolean. Choose
+            //    latter at this point
+            g.writeBoolean(Boolean.TRUE.equals(value));
+        }
+
+        @Override
+        public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException {
+            // 27-Mar-2017, tatu: As usual, bit tricky but... seems like we should call
+            //    visitor for actual representation
+            visitIntFormat(visitor, typeHint, NumberType.INT);
+        }
+
+        @Override
+        public JsonSerializer<?> createContextual(SerializerProvider serializers,
+                BeanProperty property) throws JsonMappingException
+        {
+            JsonFormat.Value format = findFormatOverrides(serializers,
+                    property, Boolean.class);
+            if (format != null) {
+                JsonFormat.Shape shape = format.getShape();
+                if (!shape.isNumeric()) {
+                    return new BooleanSerializer(_forPrimitive);
+                }
+            }
+            return this;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/ByteArraySerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/ByteArraySerializer.java
index e19ba93..69c2ae6 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/ByteArraySerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/ByteArraySerializer.java
@@ -4,6 +4,8 @@
 import java.lang.reflect.Type;
 
 import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.core.type.WritableTypeId;
 import com.fasterxml.jackson.databind.JavaType;
 import com.fasterxml.jackson.databind.JsonMappingException;
 import com.fasterxml.jackson.databind.JsonNode;
@@ -20,7 +22,7 @@
  * as numbers. Instead, we assume that it would make more sense to output content
  * as base64 encoded bytes (using default base64 encoding).
  *<p>
- * NOTE: since it is NOT serialized as an array, can not use AsArraySerializer as base
+ * NOTE: since it is NOT serialized as an array, cannot use AsArraySerializer as base
  *<p>
  * NOTE: since 2.6, has been a main-level class; earlier was embedded in
  * {@link StdArraySerializers}.
@@ -36,7 +38,7 @@
     
     @Override
     public boolean isEmpty(SerializerProvider prov, byte[] value) {
-        return (value == null) || (value.length == 0);
+        return value.length == 0;
     }
     
     @Override
@@ -52,12 +54,21 @@
             TypeSerializer typeSer)
         throws IOException
     {
+        // most likely scalar
+        WritableTypeId typeIdDef = typeSer.writeTypePrefix(g,
+                typeSer.typeId(value, JsonToken.VALUE_EMBEDDED_OBJECT));
+        g.writeBinary(provider.getConfig().getBase64Variant(),
+                value, 0, value.length);
+        typeSer.writeTypeSuffix(g, typeIdDef);
+
+        /* OLD impl
         typeSer.writeTypePrefixForScalar(value, g);
         g.writeBinary(provider.getConfig().getBase64Variant(),
                 value, 0, value.length);
         typeSer.writeTypeSuffixForScalar(value, g);
+        */
     }
-    
+
     @Override
     public JsonNode getSchema(SerializerProvider provider, Type typeHint)
     {
@@ -65,7 +76,7 @@
         ObjectNode itemSchema = createSchemaNode("byte"); //binary values written as strings?
         return o.set("items", itemSchema);
     }
-    
+
     @Override
     public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
         throws JsonMappingException
@@ -76,11 +87,9 @@
         //
         // TODO: for 2.8, make work either as String/base64, or array of numbers,
         //   with a qualifier that can be used to determine it's byte[]
-        if (visitor != null) {
-            JsonArrayFormatVisitor v2 = visitor.expectArrayFormat(typeHint);
-            if (v2 != null) {
-                v2.itemsFormat(JsonFormatTypes.INTEGER);
-            }
+        JsonArrayFormatVisitor v2 = visitor.expectArrayFormat(typeHint);
+        if (v2 != null) {
+            v2.itemsFormat(JsonFormatTypes.INTEGER);
         }
     }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/ByteBufferSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/ByteBufferSerializer.java
index e838001..d381b2b 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/ByteBufferSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/ByteBufferSerializer.java
@@ -5,6 +5,9 @@
 
 import com.fasterxml.jackson.core.*;
 import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonArrayFormatVisitor;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
 import com.fasterxml.jackson.databind.util.ByteBufferBackedInputStream;
 
 @SuppressWarnings("serial")
@@ -30,4 +33,15 @@
         gen.writeBinary(in, copy.remaining());
         in.close();
     }
+
+    @Override // since 2.9
+    public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+        throws JsonMappingException
+    {
+        // 31-Mar-2017, tatu: Use same type as `ByteArraySerializer`: not optimal but has to do
+        JsonArrayFormatVisitor v2 = visitor.expectArrayFormat(typeHint);
+        if (v2 != null) {
+            v2.itemsFormat(JsonFormatTypes.INTEGER);
+        }
+    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/CalendarSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/CalendarSerializer.java
index ff93ba3..3140352 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/CalendarSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/CalendarSerializer.java
@@ -37,19 +37,12 @@
     }
 
     @Override
-    public void serialize(Calendar value, JsonGenerator jgen, SerializerProvider provider) throws IOException
+    public void serialize(Calendar value, JsonGenerator g, SerializerProvider provider) throws IOException
     {
         if (_asTimestamp(provider)) {
-            jgen.writeNumber(_timestamp(value));
-        } else if (_customFormat != null) {
-            // 21-Feb-2011, tatu: not optimal, but better than alternatives:
-            synchronized (_customFormat) {
-                // _customformat cannot parse Calendar, so Date should be passed
-                jgen.writeString(_customFormat.format(value.getTime()));
-            }
-        } else {
-            provider.defaultSerializeDateValue(value.getTime(), jgen);
+            g.writeNumber(_timestamp(value));
+            return;
         }
+        _serializeAsString(value.getTime(), g, provider);
     }
-
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/CollectionSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/CollectionSerializer.java
index 7cb1885..6dec6a7 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/CollectionSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/CollectionSerializer.java
@@ -77,17 +77,12 @@
 
     @Override
     public boolean isEmpty(SerializerProvider prov, Collection<?> value) {
-        return (value == null) || value.isEmpty();
+        return value.isEmpty();
     }
 
     @Override
     public boolean hasSingleElement(Collection<?> value) {
-        Iterator<?> it = value.iterator();
-        if (!it.hasNext()) {
-            return false;
-        }
-        it.next();
-        return !it.hasNext();
+        return value.size() == 1;
     }
 
     /*
@@ -97,27 +92,28 @@
      */
 
     @Override
-    public final void serialize(Collection<?> value, JsonGenerator jgen, SerializerProvider provider) throws IOException
+    public final void serialize(Collection<?> value, JsonGenerator g, SerializerProvider provider) throws IOException
     {
         final int len = value.size();
         if (len == 1) {
             if (((_unwrapSingle == null) &&
                     provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED))
                     || (_unwrapSingle == Boolean.TRUE)) {
-                serializeContents(value, jgen, provider);
+                serializeContents(value, g, provider);
                 return;
             }
         }
-        jgen.writeStartArray(len);
-        serializeContents(value, jgen, provider);
-        jgen.writeEndArray();
+        g.writeStartArray(len);
+        serializeContents(value, g, provider);
+        g.writeEndArray();
     }
     
     @Override
-    public void serializeContents(Collection<?> value, JsonGenerator jgen, SerializerProvider provider) throws IOException
+    public void serializeContents(Collection<?> value, JsonGenerator g, SerializerProvider provider) throws IOException
     {
+        g.setCurrentValue(value);
         if (_elementSerializer != null) {
-            serializeContentsUsing(value, jgen, provider, _elementSerializer);
+            serializeContentsUsing(value, g, provider, _elementSerializer);
             return;
         }
         Iterator<?> it = value.iterator();
@@ -132,7 +128,7 @@
             do {
                 Object elem = it.next();
                 if (elem == null) {
-                    provider.defaultSerializeNull(jgen);
+                    provider.defaultSerializeNull(g);
                 } else {
                     Class<?> cc = elem.getClass();
                     JsonSerializer<Object> serializer = serializers.serializerFor(cc);
@@ -146,9 +142,9 @@
                         serializers = _dynamicSerializers;
                     }
                     if (typeSer == null) {
-                        serializer.serialize(elem, jgen, provider);
+                        serializer.serialize(elem, g, provider);
                     } else {
-                        serializer.serializeWithType(elem, jgen, provider, typeSer);
+                        serializer.serializeWithType(elem, g, provider, typeSer);
                     }
                 }
                 ++i;
@@ -158,9 +154,8 @@
         }
     }
 
-    public void serializeContentsUsing(Collection<?> value, JsonGenerator jgen, SerializerProvider provider,
-            JsonSerializer<Object> ser)
-        throws IOException, JsonGenerationException
+    public void serializeContentsUsing(Collection<?> value, JsonGenerator g, SerializerProvider provider,
+            JsonSerializer<Object> ser) throws IOException
     {
         Iterator<?> it = value.iterator();
         if (it.hasNext()) {
@@ -170,12 +165,12 @@
                 Object elem = it.next();
                 try {
                     if (elem == null) {
-                        provider.defaultSerializeNull(jgen);
+                        provider.defaultSerializeNull(g);
                     } else {
                         if (typeSer == null) {
-                            ser.serialize(elem, jgen, provider);
+                            ser.serialize(elem, g, provider);
                         } else {
-                            ser.serializeWithType(elem, jgen, provider, typeSer);
+                            ser.serializeWithType(elem, g, provider, typeSer);
                         }
                     }
                     ++i;
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/DateSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/DateSerializer.java
index 3138ae7..028d7d6 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/DateSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/DateSerializer.java
@@ -42,17 +42,12 @@
     }
 
     @Override
-    public void serialize(Date value, JsonGenerator gen, SerializerProvider provider) throws IOException
+    public void serialize(Date value, JsonGenerator g, SerializerProvider provider) throws IOException
     {
         if (_asTimestamp(provider)) {
-            gen.writeNumber(_timestamp(value));
-        } else if (_customFormat != null) {
-            // 21-Feb-2011, tatu: not optimal, but better than alternatives:
-            synchronized (_customFormat) {
-                gen.writeString(_customFormat.format(value));
-            }
-        } else {
-            provider.defaultSerializeDateValue(value, gen);
+            g.writeNumber(_timestamp(value));
+            return;
         }
+        _serializeAsString(value, g, provider);
     }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/DateTimeSerializerBase.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/DateTimeSerializerBase.java
index 4b9312b..5b5d5d0 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/DateTimeSerializerBase.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/DateTimeSerializerBase.java
@@ -4,8 +4,10 @@
 import java.lang.reflect.Type;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
+import java.util.Date;
 import java.util.Locale;
 import java.util.TimeZone;
+import java.util.concurrent.atomic.AtomicReference;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
 
@@ -35,12 +37,23 @@
      */
     protected final DateFormat _customFormat;
 
+    /**
+     * If {@link #_customFormat} is used, we will try to reuse instances in simplest
+     * possible form; thread-safe, but without overhead of <code>ThreadLocal</code>
+     * (not from code, but wrt retaining of possibly large number of format instances
+     * over all threads, properties with custom formats).
+     *
+     * @since 2.9
+     */
+    protected final AtomicReference<DateFormat> _reusedCustomFormat;
+
     protected DateTimeSerializerBase(Class<T> type,
             Boolean useTimestamp, DateFormat customFormat)
     {
         super(type);
         _useTimestamp = useTimestamp;
         _customFormat = customFormat;
+        _reusedCustomFormat = (customFormat == null) ? null : new AtomicReference<DateFormat>();
     }
 
     public abstract DateTimeSerializerBase<T> withFormat(Boolean timestamp, DateFormat customFormat);
@@ -101,10 +114,9 @@
         //    mechanism for changing `DateFormat` instances (or even clone()ing)
         //    So: require it be `SimpleDateFormat`; can't config other types
         if (!(df0 instanceof SimpleDateFormat)) {
-//            serializers.reportBadDefinition(handledType(), String.format(
-            serializers.reportMappingProblem(
-"Configured `DateFormat` (%s) not a `SimpleDateFormat`; can not configure `Locale` or `TimeZone`",
-df0.getClass().getName());
+            serializers.reportBadDefinition(handledType(), String.format(
+"Configured `DateFormat` (%s) not a `SimpleDateFormat`; cannot configure `Locale` or `TimeZone`",
+df0.getClass().getName()));
         }
         SimpleDateFormat df = (SimpleDateFormat) df0;
         if (hasLocale) {
@@ -127,21 +139,16 @@
     /**********************************************************
      */
 
-    @Deprecated
-    @Override
-    public boolean isEmpty(T value) {
-        // let's assume "null date" (timestamp 0) qualifies for empty
-        return (value == null) || (_timestamp(value) == 0L);
-    }
-
     @Override
     public boolean isEmpty(SerializerProvider serializers, T value) {
-        // let's assume "null date" (timestamp 0) qualifies for empty
-        return (value == null) || (_timestamp(value) == 0L);
+        // 09-Mar-2017, tatu: as per [databind#1550] timestamp 0 is NOT "empty"; but
+        //   with versions up to 2.8.x this was the case. Fixed for 2.9.
+//        return _timestamp(value) == 0L;
+        return false;
     }
-    
+
     protected abstract long _timestamp(T value);
-    
+
     @Override
     public JsonNode getSchema(SerializerProvider serializers, Type typeHint) {
         //todo: (ryan) add a format for the date in the schema?
@@ -195,4 +202,29 @@
             visitStringFormat(visitor, typeHint, JsonValueFormat.DATE_TIME);
         }
     }
+
+    /**
+     * @since 2.9
+     */
+    protected void _serializeAsString(Date value, JsonGenerator g, SerializerProvider provider) throws IOException
+    {
+        if (_customFormat == null) {
+            provider.defaultSerializeDateValue(value, g);
+            return;
+        }
+
+        // 19-Jul-2017, tatu: Here we will try a simple but (hopefully) effective mechanism for
+        //    reusing formatter instance. This is our second attempt, after initially trying simple
+        //    synchronization (which turned out to be bottleneck for some users in production...).
+        //    While `ThreadLocal` could alternatively be used, it is likely that it would lead to
+        //    higher memory footprint, but without much upside -- if we can not reuse, we'll just
+        //    clone(), which has some overhead but not drastic one.
+        
+        DateFormat f = _reusedCustomFormat.getAndSet(null);
+        if (f == null) {
+            f = (DateFormat) _customFormat.clone();
+        }
+        g.writeString(f.format(value));
+        _reusedCustomFormat.compareAndSet(null, f);
+    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSerializer.java
index dae384a..def97ee 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSerializer.java
@@ -52,14 +52,6 @@
     /* Construction, initialization
     /**********************************************************
      */
-    
-    /**
-     * @deprecated Since 2.1
-     */
-    @Deprecated
-    public EnumSerializer(EnumValues v) {
-        this(v, null);
-    }
 
     public EnumSerializer(EnumValues v, Boolean serializeAsIndex)
     {
@@ -78,7 +70,7 @@
     public static EnumSerializer construct(Class<?> enumClass, SerializationConfig config,
             BeanDescription beanDesc, JsonFormat.Value format)
     {
-        /* 08-Apr-2015, tatu: As per [databind#749], we can not statically determine
+        /* 08-Apr-2015, tatu: As per [databind#749], we cannot statically determine
          *   between name() and toString(), need to construct `EnumValues` with names,
          *   handle toString() case dynamically (for example)
          */
@@ -96,15 +88,14 @@
     public JsonSerializer<?> createContextual(SerializerProvider serializers,
             BeanProperty property) throws JsonMappingException
     {
-        if (property != null) {
-            JsonFormat.Value format = findFormatOverrides(serializers,
-                    property, handledType());
-            if (format != null) {
-                Boolean serializeAsIndex = _isShapeWrittenUsingIndex(property.getType().getRawClass(),
-                        format, false, _serializeAsIndex);
-                if (serializeAsIndex != _serializeAsIndex) {
-                    return new EnumSerializer(_values, serializeAsIndex);
-                }
+        JsonFormat.Value format = findFormatOverrides(serializers,
+                property, handledType());
+        if (format != null) {
+            Class<?> type = handledType();
+            Boolean serializeAsIndex = _isShapeWrittenUsingIndex(type,
+                    format, false, _serializeAsIndex);
+            if (serializeAsIndex != _serializeAsIndex) {
+                return new EnumSerializer(_values, serializeAsIndex);
             }
         }
         return this;
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSetSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSetSerializer.java
index af522dd..6211d71 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSetSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSetSerializer.java
@@ -18,14 +18,6 @@
         super(EnumSet.class, elemType, true, null, null);
     }
 
-    /**
-     * @deprecated since 2.6
-     */
-    @Deprecated // since 2.6
-    public EnumSetSerializer(JavaType elemType, BeanProperty property) {
-        this(elemType);
-    }
-
     public EnumSetSerializer(EnumSetSerializer src,
             BeanProperty property, TypeSerializer vts, JsonSerializer<?> valueSerializer,
             Boolean unwrapSingle) {
@@ -47,7 +39,7 @@
     
     @Override
     public boolean isEmpty(SerializerProvider prov, EnumSet<? extends Enum<?>> value) {
-        return (value == null) || value.isEmpty();
+        return value.isEmpty();
     }
 
     @Override
@@ -59,7 +51,7 @@
     public final void serialize(EnumSet<? extends Enum<?>> value, JsonGenerator gen,
             SerializerProvider provider) throws IOException
     {
-    	final int len = value.size();
+        final int len = value.size();
         if (len == 1) {
             if (((_unwrapSingle == null)
                     && provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED))
@@ -85,7 +77,7 @@
          */
         for (Enum<?> en : value) {
             if (enumSer == null) {
-                /* 12-Jan-2010, tatu: Since enums can not be polymorphic, let's
+                /* 12-Jan-2010, tatu: Since enums cannot be polymorphic, let's
                  *   not bother with typed serializer variant here
                  */
                 enumSer = provider.findValueSerializer(en.getDeclaringClass(), _property);
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/InetAddressSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/InetAddressSerializer.java
index 3371481..2bc0e14 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/InetAddressSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/InetAddressSerializer.java
@@ -3,42 +3,96 @@
 import java.io.IOException;
 import java.net.InetAddress;
 
+import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.type.WritableTypeId;
+import com.fasterxml.jackson.databind.BeanProperty;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.JsonSerializer;
 import com.fasterxml.jackson.databind.SerializerProvider;
 import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+import com.fasterxml.jackson.databind.ser.ContextualSerializer;
 
 /**
  * Simple serializer for {@link java.net.InetAddress}. Main complexity is
  * with registration, since same serializer is to be used for sub-classes.
+ *<p>
+ * Since 2.9 allows use of {@link JsonFormat} configuration (annotation,
+ * per-type defaulting) so that if <code>JsonFormat.Shape.NUMBER</code>
+ * (or <code>ARRAY</code>) is used, will serialize as "host address"
+ * (dotted numbers) instead of simple conversion.
  */
 @SuppressWarnings("serial")
 public class InetAddressSerializer
     extends StdScalarSerializer<InetAddress>
+    implements ContextualSerializer
 {
-    public InetAddressSerializer() { super(InetAddress.class); }
+    /**
+     * @since 2.9
+     */
+    protected final boolean _asNumeric;
 
+    public InetAddressSerializer() {
+        this(false);
+    }
+
+    /**
+     * @since 2.9
+     */
+    public InetAddressSerializer(boolean asNumeric) {
+        super(InetAddress.class);
+        _asNumeric = asNumeric;
+    }
+    
     @Override
-    public void serialize(InetAddress value, JsonGenerator jgen, SerializerProvider provider) throws IOException
+    public JsonSerializer<?> createContextual(SerializerProvider serializers,
+            BeanProperty property) throws JsonMappingException
     {
-        // Ok: get textual description; choose "more specific" part
-        String str = value.toString().trim();
-        int ix = str.indexOf('/');
-        if (ix >= 0) {
-            if (ix == 0) { // missing host name; use address
-                str = str.substring(1);
-            } else { // otherwise use name
-                str = str.substring(0, ix);
+        JsonFormat.Value format = findFormatOverrides(serializers,
+                property, handledType());
+        boolean asNumeric = false;
+        if (format != null) {
+            JsonFormat.Shape shape = format.getShape();
+            if (shape.isNumeric() || shape == JsonFormat.Shape.ARRAY) {
+                asNumeric = true;
             }
         }
-        jgen.writeString(str);
+        if (asNumeric != _asNumeric) {
+            return new InetAddressSerializer(asNumeric);
+        }
+        return this;
     }
 
     @Override
-    public void serializeWithType(InetAddress value, JsonGenerator jgen, SerializerProvider provider, TypeSerializer typeSer) throws IOException, JsonGenerationException
+    public void serialize(InetAddress value, JsonGenerator g, SerializerProvider provider) throws IOException
+    {
+        String str;
+
+        if (_asNumeric) { // since 2.9
+            str = value.getHostAddress();
+        } else {
+            // Ok: get textual description; choose "more specific" part
+            str = value.toString().trim();
+            int ix = str.indexOf('/');
+            if (ix >= 0) {
+                if (ix == 0) { // missing host name; use address
+                    str = str.substring(1);
+                } else { // otherwise use name
+                    str = str.substring(0, ix);
+                }
+            }
+        }
+        g.writeString(str);
+    }
+
+    @Override
+    public void serializeWithType(InetAddress value, JsonGenerator g,
+            SerializerProvider provider, TypeSerializer typeSer) throws IOException
     {
         // Better ensure we don't use specific sub-classes...
-        typeSer.writeTypePrefixForScalar(value, jgen, InetAddress.class);
-        serialize(value, jgen, provider);
-        typeSer.writeTypeSuffixForScalar(value, jgen);
+        WritableTypeId typeIdDef = typeSer.writeTypePrefix(g,
+                typeSer.typeId(value, InetAddress.class, JsonToken.VALUE_STRING));
+        serialize(value, g, provider);
+        typeSer.writeTypeSuffix(g, typeIdDef);
     }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/InetSocketAddressSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/InetSocketAddressSerializer.java
index 8dcbac9..13b8be8 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/InetSocketAddressSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/InetSocketAddressSerializer.java
@@ -1,15 +1,14 @@
 package com.fasterxml.jackson.databind.ser.std;
 
-import com.fasterxml.jackson.core.JsonGenerationException;
+import java.io.IOException;
+import java.net.*;
+
 import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.core.type.WritableTypeId;
 import com.fasterxml.jackson.databind.SerializerProvider;
 import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
 
-import java.io.IOException;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-
 /**
  * Simple serializer for {@link InetSocketAddress}.
  */
@@ -40,11 +39,13 @@
     }
 
     @Override
-    public void serializeWithType(InetSocketAddress value, JsonGenerator jgen, SerializerProvider provider, TypeSerializer typeSer) throws IOException, JsonGenerationException
+    public void serializeWithType(InetSocketAddress value, JsonGenerator g,
+            SerializerProvider provider, TypeSerializer typeSer) throws IOException
     {
         // Better ensure we don't use specific sub-classes...
-        typeSer.writeTypePrefixForScalar(value, jgen, InetSocketAddress.class);
-        serialize(value, jgen, provider);
-        typeSer.writeTypeSuffixForScalar(value, jgen);
+        WritableTypeId typeIdDef = typeSer.writeTypePrefix(g,
+                typeSer.typeId(value, InetSocketAddress.class, JsonToken.VALUE_STRING));
+        serialize(value, g, provider);
+        typeSer.writeTypeSuffix(g, typeIdDef);
     }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/IterableSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/IterableSerializer.java
index 6c4082d..5cc5068 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/IterableSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/IterableSerializer.java
@@ -40,7 +40,7 @@
     @Override
     public boolean isEmpty(SerializerProvider prov, Iterable<?> value) {
         // Not really good way to implement this, but has to do for now:
-        return (value == null) || !value.iterator().hasNext();
+        return !value.iterator().hasNext();
     }
 
     @Override
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/JsonValueSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/JsonValueSerializer.java
index 7582c3a..6112a7d 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/JsonValueSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/JsonValueSerializer.java
@@ -8,9 +8,10 @@
 
 import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
 import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.type.WritableTypeId;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
-import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
+import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitable;
 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonStringFormatVisitor;
@@ -19,6 +20,7 @@
 import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
 import com.fasterxml.jackson.databind.ser.BeanSerializer;
 import com.fasterxml.jackson.databind.ser.ContextualSerializer;
+import com.fasterxml.jackson.databind.util.ClassUtil;
 
 /**
  * Serializer class that can serialize Object that have a
@@ -37,11 +39,11 @@
 public class JsonValueSerializer
     extends StdSerializer<Object>
     implements ContextualSerializer, JsonFormatVisitable, SchemaAware
-    {
+{
     /**
-     * @since 2.8 (was "plain" method before)
+     * @since 2.9
      */
-    protected final AnnotatedMethod _accessorMethod;
+    protected final AnnotatedMember _accessor;
 
     protected final JsonSerializer<Object> _valueSerializer;
 
@@ -71,10 +73,10 @@
      *    to information we need
      */
     @SuppressWarnings("unchecked")
-    public JsonValueSerializer(AnnotatedMethod valueMethod, JsonSerializer<?> ser)
+    public JsonValueSerializer(AnnotatedMember accessor, JsonSerializer<?> ser)
     {
-        super(valueMethod.getType());
-        _accessorMethod = valueMethod;
+        super(accessor.getType());
+        _accessor = accessor;
         _valueSerializer = (JsonSerializer<Object>) ser;
         _property = null;
         _forceTypeInformation = true; // gets reconsidered when we are contextualized
@@ -85,7 +87,7 @@
             JsonSerializer<?> ser, boolean forceTypeInfo)
     {
         super(_notNullClass(src.handledType()));
-        _accessorMethod = src._accessorMethod;
+        _accessor = src._accessor;
         _valueSerializer = (JsonSerializer<Object>) ser;
         _property = property;
         _forceTypeInformation = forceTypeInfo;
@@ -127,7 +129,7 @@
              * if not, we don't really know the actual type until we get the instance.
              */
             // 10-Mar-2010, tatu: Except if static typing is to be used
-            JavaType t = _accessorMethod.getType();
+            JavaType t = _accessor.getType();
             if (provider.isEnabled(MapperFeature.USE_STATIC_TYPING) || t.isFinal()) {
                 // false -> no need to cache
                 /* 10-Mar-2010, tatu: Ideally we would actually separate out type
@@ -161,7 +163,7 @@
     public void serialize(Object bean, JsonGenerator gen, SerializerProvider prov) throws IOException
     {
         try {
-            Object value = _accessorMethod.getValue(bean);
+            Object value = _accessor.getValue(bean);
             if (value == null) {
                 prov.defaultSerializeNull(gen);
                 return;
@@ -177,20 +179,8 @@
                 ser = prov.findTypedValueSerializer(c, true, _property);
             }
             ser.serialize(value, gen, prov);
-        } catch (IOException ioe) {
-            throw ioe;
         } catch (Exception e) {
-            Throwable t = e;
-            // Need to unwrap this specific type, to see infinite recursion...
-            while (t instanceof InvocationTargetException && t.getCause() != null) {
-                t = t.getCause();
-            }
-            // Errors shouldn't be wrapped (and often can't, as well)
-            if (t instanceof Error) {
-                throw (Error) t;
-            }
-            // let's try to indicate the path best we can...
-            throw JsonMappingException.wrapWithPath(t, bean, _accessorMethod.getName() + "()");
+            wrapAndThrow(prov, e, bean, _accessor.getName() + "()");
         }
     }
 
@@ -201,7 +191,7 @@
         // Regardless of other parts, first need to find value to serialize:
         Object value = null;
         try {
-            value = _accessorMethod.getValue(bean);
+            value = _accessor.getValue(bean);
             // and if we got null, can also just write it directly
             if (value == null) {
                 provider.defaultSerializeNull(gen);
@@ -209,16 +199,17 @@
             }
             JsonSerializer<Object> ser = _valueSerializer;
             if (ser == null) { // no serializer yet? Need to fetch
-//                ser = provider.findTypedValueSerializer(value.getClass(), true, _property);
                 ser = provider.findValueSerializer(value.getClass(), _property);
             } else {
-                /* 09-Dec-2010, tatu: To work around natural type's refusal to add type info, we do
-                 *    this (note: type is for the wrapper type, not enclosed value!)
-                 */
+                // 09-Dec-2010, tatu: To work around natural type's refusal to add type info, we do
+                //    this (note: type is for the wrapper type, not enclosed value!)
                 if (_forceTypeInformation) {
-                    typeSer0.writeTypePrefixForScalar(bean, gen);
+                    // Confusing? Type id is for POJO and NOT for value returned by JsonValue accessor...
+                    WritableTypeId typeIdDef = typeSer0.writeTypePrefix(gen,
+                            typeSer0.typeId(bean, JsonToken.VALUE_STRING));
                     ser.serialize(value, gen, provider);
-                    typeSer0.writeTypeSuffixForScalar(bean, gen);
+                    typeSer0.writeTypeSuffix(gen, typeIdDef);
+
                     return;
                 }
             }
@@ -227,20 +218,8 @@
             //    (delegat type).
             TypeSerializerRerouter rr = new TypeSerializerRerouter(typeSer0, bean);
             ser.serializeWithType(value, gen, provider, rr);
-        } catch (IOException ioe) {
-            throw ioe;
         } catch (Exception e) {
-            Throwable t = e;
-            // Need to unwrap this specific type, to see infinite recursion...
-            while (t instanceof InvocationTargetException && t.getCause() != null) {
-                t = t.getCause();
-            }
-            // Errors shouldn't be wrapped (and often can't, as well)
-            if (t instanceof Error) {
-                throw (Error) t;
-            }
-            // let's try to indicate the path best we can...
-            throw JsonMappingException.wrapWithPath(t, bean, _accessorMethod.getName() + "()");
+            wrapAndThrow(provider, e, bean, _accessor.getName() + "()");
         }
     }
     
@@ -268,8 +247,8 @@
          *    
          *    Note that meaning of JsonValue, then, is very different for Enums. Sigh.
          */
-        final JavaType type = _accessorMethod.getType();
-        Class<?> declaring = _accessorMethod.getDeclaringClass();
+        final JavaType type = _accessor.getType();
+        Class<?> declaring = _accessor.getDeclaringClass();
         if ((declaring != null) && declaring.isEnum()) {
             if (_acceptJsonFormatVisitorForEnum(visitor, typeHint, declaring)) {
                 return;
@@ -283,7 +262,7 @@
                 return;
             }
         }
-        ser.acceptJsonFormatVisitor(visitor, null); 
+        ser.acceptJsonFormatVisitor(visitor, type);
     }
 
     /**
@@ -308,16 +287,14 @@
                     // 21-Apr-2016, tatu: This is convoluted to the max, but essentially we
                     //   call `@JsonValue`-annotated accessor method on all Enum members,
                     //   so it all "works out". To some degree.
-                    enums.add(String.valueOf(_accessorMethod.callOn(en)));
+                    enums.add(String.valueOf(_accessor.getValue(en)));
                 } catch (Exception e) {
                     Throwable t = e;
                     while (t instanceof InvocationTargetException && t.getCause() != null) {
                         t = t.getCause();
                     }
-                    if (t instanceof Error) {
-                        throw (Error) t;
-                    }
-                    throw JsonMappingException.wrapWithPath(t, en, _accessorMethod.getName() + "()");
+                    ClassUtil.throwIfError(t);
+                    throw JsonMappingException.wrapWithPath(t, en, _accessor.getName() + "()");
                 }
             }
             stringVisitor.enumTypes(enums);
@@ -349,7 +326,7 @@
 
     @Override
     public String toString() {
-        return "(@JsonValue serializer for method " + _accessorMethod.getDeclaringClass() + "#" + _accessorMethod.getName() + ")";
+        return "(@JsonValue serializer for method " + _accessor.getDeclaringClass() + "#" + _accessor.getName() + ")";
     }
 
     /*
@@ -394,78 +371,118 @@
             return _typeSerializer.getTypeIdResolver();
         }
 
+        // // // New Write API, 2.9+
+        
+        @Override // since 2.9
+        public WritableTypeId writeTypePrefix(JsonGenerator g,
+                WritableTypeId typeId) throws IOException {
+            // 28-Jun-2017, tatu: Important! Need to "override" value
+            typeId.forValue = _forObject;
+            return _typeSerializer.writeTypePrefix(g, typeId);
+        }
+
+        @Override // since 2.9
+        public WritableTypeId writeTypeSuffix(JsonGenerator g,
+                WritableTypeId typeId) throws IOException {
+            // NOTE: already overwrote value object so:
+            return _typeSerializer.writeTypeSuffix(g, typeId);
+        }
+
+        // // // Old Write API, pre-2.9
+
         @Override
+        @Deprecated
         public void writeTypePrefixForScalar(Object value, JsonGenerator gen) throws IOException {
             _typeSerializer.writeTypePrefixForScalar(_forObject, gen);
         }
 
         @Override
+        @Deprecated
         public void writeTypePrefixForObject(Object value, JsonGenerator gen) throws IOException {
             _typeSerializer.writeTypePrefixForObject(_forObject, gen);
         }
 
         @Override
+        @Deprecated
         public void writeTypePrefixForArray(Object value, JsonGenerator gen) throws IOException {
             _typeSerializer.writeTypePrefixForArray(_forObject, gen);
         }
 
         @Override
+        @Deprecated
         public void writeTypeSuffixForScalar(Object value, JsonGenerator gen) throws IOException {
             _typeSerializer.writeTypeSuffixForScalar(_forObject, gen);
         }
 
         @Override
+        @Deprecated
         public void writeTypeSuffixForObject(Object value, JsonGenerator gen) throws IOException {
             _typeSerializer.writeTypeSuffixForObject(_forObject, gen);
         }
 
         @Override
+        @Deprecated
         public void writeTypeSuffixForArray(Object value, JsonGenerator gen) throws IOException {
             _typeSerializer.writeTypeSuffixForArray(_forObject, gen);
         }
 
         @Override
+        @Deprecated
         public void writeTypePrefixForScalar(Object value, JsonGenerator gen, Class<?> type) throws IOException {
             _typeSerializer.writeTypePrefixForScalar(_forObject, gen, type);
         }
 
         @Override
+        @Deprecated
         public void writeTypePrefixForObject(Object value, JsonGenerator gen, Class<?> type) throws IOException {
             _typeSerializer.writeTypePrefixForObject(_forObject, gen, type);
         }
 
         @Override
+        @Deprecated
         public void writeTypePrefixForArray(Object value, JsonGenerator gen, Class<?> type) throws IOException {
             _typeSerializer.writeTypePrefixForArray(_forObject, gen, type);
         }
-        
+
+        /*
+        /**********************************************************
+        /* Deprecated methods (since 2.9)
+        /**********************************************************
+         */
+
         @Override
+        @Deprecated
         public void writeCustomTypePrefixForScalar(Object value, JsonGenerator gen, String typeId)
                 throws IOException {
             _typeSerializer.writeCustomTypePrefixForScalar(_forObject, gen, typeId);
         }
 
         @Override
+        @Deprecated
         public void writeCustomTypePrefixForObject(Object value, JsonGenerator gen, String typeId) throws IOException {
             _typeSerializer.writeCustomTypePrefixForObject(_forObject, gen, typeId);
         }
 
         @Override
+        @Deprecated
         public void writeCustomTypePrefixForArray(Object value, JsonGenerator gen, String typeId) throws IOException {
             _typeSerializer.writeCustomTypePrefixForArray(_forObject, gen, typeId);
         }
 
         @Override
+        @Deprecated
         public void writeCustomTypeSuffixForScalar(Object value, JsonGenerator gen, String typeId) throws IOException {
             _typeSerializer.writeCustomTypeSuffixForScalar(_forObject, gen, typeId);
         }
 
         @Override
+        @Deprecated
         public void writeCustomTypeSuffixForObject(Object value, JsonGenerator gen, String typeId) throws IOException {
             _typeSerializer.writeCustomTypeSuffixForObject(_forObject, gen, typeId);
         }
 
         @Override
+        @Deprecated
         public void writeCustomTypeSuffixForArray(Object value, JsonGenerator gen, String typeId) throws IOException {
             _typeSerializer.writeCustomTypeSuffixForArray(_forObject, gen, typeId);
         }
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/MapProperty.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/MapProperty.java
index 44e94c8..c6ee247 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/MapProperty.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/MapProperty.java
@@ -4,19 +4,13 @@
 import java.lang.annotation.Annotation;
 
 import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.databind.BeanProperty;
-import com.fasterxml.jackson.databind.JavaType;
-import com.fasterxml.jackson.databind.JsonMappingException;
-import com.fasterxml.jackson.databind.JsonSerializer;
-import com.fasterxml.jackson.databind.PropertyMetadata;
-import com.fasterxml.jackson.databind.PropertyName;
-import com.fasterxml.jackson.databind.SerializerProvider;
+
+import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor;
 import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.fasterxml.jackson.databind.ser.PropertyWriter;
-import com.fasterxml.jackson.databind.type.TypeFactory;
 
 /**
  * Helper class needed to support flexible filtering of Map properties
@@ -28,11 +22,13 @@
 {
     private static final long serialVersionUID = 1L;
 
+    private final static BeanProperty BOGUS_PROP = new BeanProperty.Bogus();
+    
     protected final TypeSerializer _typeSerializer;
 
     protected final BeanProperty _property;
 
-    protected Object _key;
+    protected Object _key, _value;
 
     protected JsonSerializer<Object> _keySerializer, _valueSerializer;
 
@@ -40,21 +36,31 @@
     {
         super((prop == null) ? PropertyMetadata.STD_REQUIRED_OR_OPTIONAL : prop.getMetadata());
         _typeSerializer = typeSer;
-        _property = prop;
+        _property = (prop == null) ? BOGUS_PROP : prop;
     }
 
     /**
      * Initialization method that needs to be called before passing
      * property to filter.
+     *
+     * @since 2.9
      */
-    public void reset(Object key,
+    public void reset(Object key, Object value,
             JsonSerializer<Object> keySer, JsonSerializer<Object> valueSer)
     {
         _key = key;
+        _value = value;
         _keySerializer = keySer;
         _valueSerializer = valueSer;
     }
-    
+
+    @Deprecated // since 2.9
+    public void reset(Object key,
+            JsonSerializer<Object> keySer, JsonSerializer<Object> valueSer)
+    {
+        reset(key, _value, keySer, valueSer);
+    }
+
     @Override
     public String getName() {
         if (_key instanceof String) {
@@ -63,6 +69,20 @@
         return String.valueOf(_key);
     }
 
+    /**
+     * @since 2.9
+     */
+    public Object getValue() {
+        return _value;
+    }
+
+    /**
+     * @since 2.9
+     */
+    public void setValue(Object v) {
+        _value = v;
+    }
+
     @Override
     public PropertyName getFullName() {
         return new PropertyName(getName());
@@ -70,28 +90,28 @@
 
     @Override
     public <A extends Annotation> A getAnnotation(Class<A> acls) {
-        return (_property == null) ? null : _property.getAnnotation(acls);
+        return _property.getAnnotation(acls);
     }
 
     @Override
     public <A extends Annotation> A getContextAnnotation(Class<A> acls) {
-        return (_property == null) ? null : _property.getContextAnnotation(acls);
+        return _property.getContextAnnotation(acls);
     }
     
     @Override
-    public void serializeAsField(Object value, JsonGenerator gen,
+    public void serializeAsField(Object map, JsonGenerator gen,
             SerializerProvider provider) throws IOException
     {
         _keySerializer.serialize(_key, gen, provider);
         if (_typeSerializer == null) {
-            _valueSerializer.serialize(value, gen, provider);
+            _valueSerializer.serialize(_value, gen, provider);
         } else {
-            _valueSerializer.serializeWithType(value, gen, provider, _typeSerializer);
+            _valueSerializer.serializeWithType(_value, gen, provider, _typeSerializer);
         }
     }
 
     @Override
-    public void serializeAsOmittedField(Object value, JsonGenerator gen,
+    public void serializeAsOmittedField(Object map, JsonGenerator gen,
             SerializerProvider provider) throws Exception
     {
         if (!gen.canOmitFields()) {
@@ -100,13 +120,13 @@
     }
 
     @Override
-    public void serializeAsElement(Object value, JsonGenerator gen,
+    public void serializeAsElement(Object map, JsonGenerator gen,
             SerializerProvider provider) throws Exception
     {
         if (_typeSerializer == null) {
-            _valueSerializer.serialize(value, gen, provider);
+            _valueSerializer.serialize(_value, gen, provider);
         } else {
-            _valueSerializer.serializeWithType(value, gen, provider, _typeSerializer);
+            _valueSerializer.serializeWithType(_value, gen, provider, _typeSerializer);
         }
     }
     
@@ -128,9 +148,7 @@
             SerializerProvider provider)
         throws JsonMappingException
     {
-        if (_property != null) {
-            _property.depositSchemaProperty(objectVisitor, provider);
-        }
+        _property.depositSchemaProperty(objectVisitor, provider);
     }
 
     @Override
@@ -138,20 +156,20 @@
     public void depositSchemaProperty(ObjectNode propertiesNode,
             SerializerProvider provider) throws JsonMappingException {
         // nothing to do here
-   }
+    }
 
     @Override
     public JavaType getType() {
-        return (_property == null) ? TypeFactory.unknownType() : _property.getType();
+        return _property.getType();
     }
 
     @Override
     public PropertyName getWrapperName() {
-        return (_property == null) ? null : _property.getWrapperName();
+        return _property.getWrapperName();
     }
 
     @Override
     public AnnotatedMember getMember() {
-        return (_property == null) ? null : _property.getMember();
+        return _property.getMember();
     }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/MapSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/MapSerializer.java
index a3af8cc..b4d6bb9 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/MapSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/MapSerializer.java
@@ -8,6 +8,7 @@
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.type.WritableTypeId;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
 import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
@@ -20,6 +21,8 @@
 import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap;
 import com.fasterxml.jackson.databind.type.TypeFactory;
 import com.fasterxml.jackson.databind.util.ArrayBuilders;
+import com.fasterxml.jackson.databind.util.BeanUtil;
+import com.fasterxml.jackson.databind.util.ClassUtil;
 
 /**
  * Standard serializer implementation for serializing {link java.util.Map} types.
@@ -37,16 +40,22 @@
     protected final static JavaType UNSPECIFIED_TYPE = TypeFactory.unknownType();
 
     /**
+     * @since 2.9
+     */
+    public final static Object MARKER_FOR_EMPTY = JsonInclude.Include.NON_EMPTY;
+
+    /*
+    /**********************************************************
+    /* Basic information about referring property, type
+    /**********************************************************
+     */
+    
+    /**
      * Map-valued property being serialized with this instance
      */
     protected final BeanProperty _property;
 
     /**
-     * Set of entries to omit during serialization, if any
-     */
-    protected final Set<String> _ignoredEntries;
-
-    /**
      * Whether static types should be used for serialization of values
      * or not (if not, dynamic runtime type is used)
      */
@@ -62,6 +71,12 @@
      */
     protected final JavaType _valueType;
 
+    /*
+    /**********************************************************
+    /* Serializers used
+    /**********************************************************
+     */
+    
     /**
      * Key serializer to use, if it can be statically determined
      */
@@ -78,11 +93,22 @@
     protected final TypeSerializer _valueTypeSerializer;
 
     /**
-     * If value type can not be statically determined, mapping from
+     * If value type cannot be statically determined, mapping from
      * runtime value types to serializers are stored in this object.
      */
     protected PropertySerializerMap _dynamicValueSerializers;
 
+    /*
+    /**********************************************************
+    /* Config settings, filtering
+    /**********************************************************
+     */
+    
+    /**
+     * Set of entries to omit during serialization, if any
+     */
+    protected final Set<String> _ignoredEntries;
+
     /**
      * Id of the property filter to use, if any; null if none.
      *
@@ -91,17 +117,10 @@
     protected final Object _filterId;
 
     /**
-     * Flag set if output is forced to be sorted by keys (usually due
-     * to annotation).
-     * 
-     * @since 2.4
-     */
-    protected final boolean _sortKeys;
-
-    /**
      * Value that indicates suppression mechanism to use for <b>values contained</b>;
-     * either one of values of {@link com.fasterxml.jackson.annotation.JsonInclude.Include},
-     * or actual object to compare against ("default value").
+     * either "filter" (of which <code>equals()</code> is called), or marker
+     * value of {@link #MARKER_FOR_EMPTY}, or null to indicate no filtering for
+     * non-null values.
      * Note that inclusion value for Map instance itself is handled by caller (POJO
      * property that refers to the Map value).
      * 
@@ -109,6 +128,28 @@
      */
     protected final Object _suppressableValue;
 
+    /**
+     * Flag that indicates what to do with `null` values, distinct from
+     * handling of {@link #_suppressableValue}
+     *
+     * @since 2.9
+     */
+    protected final boolean _suppressNulls;
+
+    /*
+    /**********************************************************
+    /* Config settings, other
+    /**********************************************************
+     */
+
+    /**
+     * Flag set if output is forced to be sorted by keys (usually due
+     * to annotation).
+     * 
+     * @since 2.4
+     */
+    protected final boolean _sortKeys;
+
     /*
     /**********************************************************
     /* Life-cycle
@@ -138,17 +179,9 @@
         _filterId = null;
         _sortKeys = false;
         _suppressableValue = null;
+        _suppressNulls = false;
     }
 
-    /**
-     * @since 2.5
-     */
-    protected void _ensureOverride() {
-        if (getClass() != MapSerializer.class) {
-            throw new IllegalStateException("Missing override in class "+getClass().getName());
-        }
-    }
-    
     @SuppressWarnings("unchecked")
     protected MapSerializer(MapSerializer src, BeanProperty property,
             JsonSerializer<?> keySerializer, JsonSerializer<?> valueSerializer,
@@ -168,18 +201,14 @@
         _filterId = src._filterId;
         _sortKeys = src._sortKeys;
         _suppressableValue = src._suppressableValue;
-    }
-
-    @Deprecated // since 2.5
-    protected MapSerializer(MapSerializer src, TypeSerializer vts) {
-        this(src, vts, src._suppressableValue);
+        _suppressNulls = src._suppressNulls;
     }
 
     /**
-     * @since 2.5
+     * @since 2.9
      */
     protected MapSerializer(MapSerializer src, TypeSerializer vts,
-            Object suppressableValue)
+            Object suppressableValue, boolean suppressNulls)
     {
         super(Map.class, false);
         _ignoredEntries = src._ignoredEntries;
@@ -193,12 +222,8 @@
         _property = src._property;
         _filterId = src._filterId;
         _sortKeys = src._sortKeys;
-        // 05-Jun-2015, tatu: For referential, this is same as NON_EMPTY; for others, NON_NULL, so:
-        if (suppressableValue == JsonInclude.Include.NON_ABSENT) {
-            suppressableValue = _valueType.isReferenceType() ?
-                    JsonInclude.Include.NON_EMPTY : JsonInclude.Include.NON_NULL;
-        }
         _suppressableValue = suppressableValue;
+        _suppressNulls = suppressNulls;
     }
 
     protected MapSerializer(MapSerializer src, Object filterId, boolean sortKeys)
@@ -216,6 +241,7 @@
         _filterId = filterId;
         _sortKeys = sortKeys;
         _suppressableValue = src._suppressableValue;
+        _suppressNulls = src._suppressNulls;
     }
 
     @Override
@@ -223,8 +249,8 @@
         if (_valueTypeSerializer == vts) {
             return this;
         }
-        _ensureOverride();
-        return new MapSerializer(this, vts, null);
+        _ensureOverride("_withValueTypeSerializer");
+        return new MapSerializer(this, vts, _suppressableValue, _suppressNulls);
     }
 
     /**
@@ -234,7 +260,7 @@
             JsonSerializer<?> keySerializer, JsonSerializer<?> valueSerializer,
             Set<String> ignored, boolean sortKeys)
     {
-        _ensureOverride();
+        _ensureOverride("withResolved");
         MapSerializer ser = new MapSerializer(this, property, keySerializer, valueSerializer, ignored);
         if (sortKeys != ser._sortKeys) {
             ser = new MapSerializer(ser, _filterId, sortKeys);
@@ -247,7 +273,7 @@
         if (_filterId == filterId) {
             return this;
         }
-        _ensureOverride();
+        _ensureOverride("withFilterId");
         return new MapSerializer(this, filterId, _sortKeys);
     }
 
@@ -255,31 +281,14 @@
      * Mutant factory for constructing an instance with different inclusion strategy
      * for content (Map values).
      * 
-     * @since 2.5
+     * @since 2.9
      */
-    public MapSerializer withContentInclusion(Object suppressableValue) {
-        if (suppressableValue == _suppressableValue) {
+    public MapSerializer withContentInclusion(Object suppressableValue, boolean suppressNulls) {
+        if ((suppressableValue == _suppressableValue) && (suppressNulls == _suppressNulls)) {
             return this;
         }
-        _ensureOverride();
-        return new MapSerializer(this, _valueTypeSerializer, suppressableValue);
-    }                
-    
-    /**
-     * @since 2.3
-     *
-     * @deprecated Since 2.8 use the other overload
-     */
-    @Deprecated // since 2.8
-    public static MapSerializer construct(String[] ignoredList, JavaType mapType,
-            boolean staticValueType, TypeSerializer vts,
-            JsonSerializer<Object> keySerializer, JsonSerializer<Object> valueSerializer,
-            Object filterId)
-    {
-        Set<String> ignoredEntries = (ignoredList == null || ignoredList.length == 0)
-                ? null : ArrayBuilders.arrayToSet(ignoredList);
-        return construct(ignoredEntries, mapType, staticValueType, vts,
-                keySerializer, valueSerializer, filterId);
+        _ensureOverride("withContentInclusion");
+        return new MapSerializer(this, _valueTypeSerializer, suppressableValue, suppressNulls);
     }
 
     /**
@@ -302,7 +311,7 @@
         if (!staticValueType) {
             staticValueType = (valueType != null && valueType.isFinal());
         } else {
-            // also: Object.class can not be handled as static, ever
+            // also: Object.class cannot be handled as static, ever
             if (valueType.getRawClass() == Object.class) {
                 staticValueType = false;
             }
@@ -315,6 +324,62 @@
         return ser;
     }
 
+    /**
+     * @since 2.9
+     */
+    protected void _ensureOverride(String method) {
+        ClassUtil.verifyMustOverride(MapSerializer.class, this, method);
+    }
+
+    /**
+     * @since 2.5
+     */
+    @Deprecated // since 2.9
+    protected void _ensureOverride() {
+        _ensureOverride("N/A");
+    }
+
+    /*
+    /**********************************************************
+    /* Deprecated creators
+    /**********************************************************
+     */
+
+    /**
+     * @since 2.5
+     * @deprecated // since 2.9
+     */
+    @Deprecated // since 2.9
+    protected MapSerializer(MapSerializer src, TypeSerializer vts,
+            Object suppressableValue)
+    {
+        this(src, vts, suppressableValue, false);
+    }
+
+    /**
+     * @deprecated since 2.9
+     */
+    @Deprecated // since 2.9
+    public MapSerializer withContentInclusion(Object suppressableValue) {
+        return new MapSerializer(this, _valueTypeSerializer, suppressableValue, _suppressNulls);
+    }                
+
+    /**
+     * @since 2.3
+     *
+     * @deprecated Since 2.8 use the other overload
+     */
+    @Deprecated // since 2.8
+    public static MapSerializer construct(String[] ignoredList, JavaType mapType,
+            boolean staticValueType, TypeSerializer vts,
+            JsonSerializer<Object> keySerializer, JsonSerializer<Object> valueSerializer,
+            Object filterId)
+    {
+        Set<String> ignoredEntries = ArrayBuilders.arrayToSet(ignoredList);
+        return construct(ignoredEntries, mapType, staticValueType, vts,
+                keySerializer, valueSerializer, filterId);
+    }
+
     /*
     /**********************************************************
     /* Post-processing (contextualization)
@@ -330,10 +395,9 @@
         JsonSerializer<?> keySer = null;
         final AnnotationIntrospector intr = provider.getAnnotationIntrospector();
         final AnnotatedMember propertyAcc = (property == null) ? null : property.getMember();
-        Object suppressableValue = _suppressableValue;
 
         // First: if we have a property, may have property-annotation overrides
-        if ((propertyAcc != null) && (intr != null)) {
+        if (_neitherNull(propertyAcc, intr)) {
             Object serDef = intr.findKeySerializer(propertyAcc);
             if (serDef != null) {
                 keySer = provider.serializerInstance(propertyAcc, serDef);
@@ -343,17 +407,11 @@
                 ser = provider.serializerInstance(propertyAcc, serDef);
             }
         }
-
-        JsonInclude.Value inclV = findIncludeOverrides(provider, property, Map.class);
-        JsonInclude.Include incl = inclV.getContentInclusion();
-        if ((incl != null) && (incl != JsonInclude.Include.USE_DEFAULTS)) {
-            suppressableValue = incl;
-        }
         if (ser == null) {
             ser = _valueSerializer;
         }
         // [databind#124]: May have a content converter
-        ser = findConvertingContentSerializer(provider, property, ser);
+        ser = findContextualConvertingSerializer(provider, property, ser);
         if (ser == null) {
             // 30-Sep-2012, tatu: One more thing -- if explicit content type is annotated,
             //   we can consider it a static case as well.
@@ -361,8 +419,6 @@
             if (_valueTypeIsStatic && !_valueType.isJavaLangObject()) {
                 ser = provider.findValueSerializer(_valueType, property);
             }
-        } else {
-            ser = provider.handleSecondaryContextualization(ser, property);
         }
         if (keySer == null) {
             keySer = _keySerializer;
@@ -374,11 +430,11 @@
         }
         Set<String> ignored = _ignoredEntries;
         boolean sortKeys = false;
-        if ((intr != null) && (propertyAcc != null)) {
+        if (_neitherNull(propertyAcc, intr)) {
             JsonIgnoreProperties.Value ignorals = intr.findPropertyIgnorals(propertyAcc);
             if (ignorals != null){
                 Set<String> newIgnored = ignorals.findIgnoredForSerialization();
-                if ((newIgnored != null) && !newIgnored.isEmpty()) {
+                if (_nonEmpty(newIgnored)) {
                     ignored = (ignored == null) ? new HashSet<String>() : new HashSet<String>(ignored);
                     for (String str : newIgnored) {
                         ignored.add(str);
@@ -386,7 +442,7 @@
                 }
             }
             Boolean b = intr.findSerializationSortAlphabetically(propertyAcc);
-            sortKeys = (b != null) && b.booleanValue();
+            sortKeys = Boolean.TRUE.equals(b);
         }
         JsonFormat.Value format = findFormatOverrides(provider, property, Map.class);
         if (format != null) {
@@ -396,9 +452,6 @@
             }
         }
         MapSerializer mser = withResolved(property, keySer, ser, ignored, sortKeys);
-        if (suppressableValue != _suppressableValue) {
-            mser = mser.withContentInclusion(suppressableValue);
-        }
 
         // [databind#307]: allow filtering
         if (property != null) {
@@ -409,10 +462,58 @@
                     mser = mser.withFilterId(filterId);
                 }
             }
+            JsonInclude.Value inclV = property.findPropertyInclusion(provider.getConfig(), null);
+            if (inclV != null) {
+                JsonInclude.Include incl = inclV.getContentInclusion();
+
+                if (incl != JsonInclude.Include.USE_DEFAULTS) {
+                    Object valueToSuppress;
+                    boolean suppressNulls;
+                    switch (incl) {
+                    case NON_DEFAULT:
+                        valueToSuppress = BeanUtil.getDefaultValue(_valueType);
+                        suppressNulls = true;
+                        if (valueToSuppress != null) {
+                            if (valueToSuppress.getClass().isArray()) {
+                                valueToSuppress = ArrayBuilders.getArrayComparator(valueToSuppress);
+                            }
+                        }
+                        break;
+                    case NON_ABSENT:
+                        suppressNulls = true;
+                        valueToSuppress = _valueType.isReferenceType() ? MARKER_FOR_EMPTY : null;
+                        break;
+                    case NON_EMPTY:
+                        suppressNulls = true;
+                        valueToSuppress = MARKER_FOR_EMPTY;
+                        break;
+                    case CUSTOM:
+                        valueToSuppress = provider.includeFilterInstance(null, inclV.getContentFilter());
+                        if (valueToSuppress == null) { // is this legal?
+                            suppressNulls = true;
+                        } else {
+                            suppressNulls = provider.includeFilterSuppressNulls(valueToSuppress);
+                        }
+                        break;
+                    case NON_NULL:
+                        valueToSuppress = null;
+                        suppressNulls = true;
+                        break;
+                    case ALWAYS: // default
+                    default:
+                        valueToSuppress = null;
+                        // 30-Sep-2016, tatu: Should not need to check global flags here,
+                        //   if inclusion forced to be ALWAYS
+                        suppressNulls = false;
+                        break;
+                    }
+                    mser = mser.withContentInclusion(valueToSuppress, suppressNulls);
+                }
+            }
         }
         return mser;
     }
-    
+
     /*
     /**********************************************************
     /* Accessors
@@ -432,45 +533,55 @@
     @Override
     public boolean isEmpty(SerializerProvider prov, Map<?,?> value)
     {
-        if (value == null || value.isEmpty()) {
+        if (value.isEmpty()) {
             return true;
         }
+        
         // 05-Nove-2015, tatu: Simple cases are cheap, but for recursive
         //   emptiness checking we actually need to see if values are empty as well.
         Object supp = _suppressableValue;
-
-        if ((supp == null) || (supp == JsonInclude.Include.ALWAYS)) {
+        if ((supp == null) && !_suppressNulls) {
             return false;
         }
         JsonSerializer<Object> valueSer = _valueSerializer;
+        final boolean checkEmpty = (MARKER_FOR_EMPTY == supp);
         if (valueSer != null) {
             for (Object elemValue : value.values()) {
-                if ((elemValue != null) && !valueSer.isEmpty(prov, elemValue)) {
+                if (elemValue == null) {
+                    if (_suppressNulls) {
+                        continue;
+                    }
+                    return false;
+                }
+                if (checkEmpty) {
+                    if (!valueSer.isEmpty(prov, elemValue)) {
+                        return false;
+                    }
+                } else if ((supp == null) || !supp.equals(value)) {
                     return false;
                 }
             }
             return true;
         }
         // But if not statically known, try this:
-        PropertySerializerMap serializers = _dynamicValueSerializers;
         for (Object elemValue : value.values()) {
             if (elemValue == null) {
-                continue;
+                if (_suppressNulls) {
+                    continue;
+                }
+                return false;
             }
-            Class<?> cc = elemValue.getClass();
-            // 05-Nov-2015, tatu: Let's not worry about generic types here, actually;
-            //   unlikely to make any difference, but does add significant overhead
-            valueSer = serializers.serializerFor(cc);
-            if (valueSer == null) {
-                try {
-                    valueSer = _findAndAddDynamic(serializers, cc, prov);
-                } catch (JsonMappingException e) { // Ugh... can not just throw as-is, so...
-                    // 05-Nov-2015, tatu: For now, probably best not to assume empty then
+            try {
+                valueSer = _findSerializer(prov, elemValue);
+            } catch (JsonMappingException e) { // Ugh... cannot just throw as-is, so...
+                // 05-Nov-2015, tatu: For now, probably best not to assume empty then
+                return false;
+            }
+            if (checkEmpty) {
+                if (!valueSer.isEmpty(prov, elemValue)) {
                     return false;
                 }
-                serializers = _dynamicValueSerializers;
-            }
-            if (!valueSer.isEmpty(prov, elemValue)) {
+            } else if ((supp == null) || !supp.equals(value)) {
                 return false;
             }
         }
@@ -481,7 +592,7 @@
     public boolean hasSingleElement(Map<?,?> value) {
         return (value.size() == 1);
     }
-    
+
     /*
     /**********************************************************
     /* Extended API
@@ -514,22 +625,14 @@
     {
         gen.writeStartObject(value);
         if (!value.isEmpty()) {
-            Object suppressableValue = _suppressableValue;
-            if (suppressableValue == JsonInclude.Include.ALWAYS) {
-                suppressableValue = null;
-            } else if (suppressableValue == null) {
-                if (!provider.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES)) {
-                    suppressableValue = JsonInclude.Include.NON_NULL;
-                }
-            }
             if (_sortKeys || provider.isEnabled(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)) {
-                value = _orderEntries(value, gen, provider, suppressableValue);
+                value = _orderEntries(value, gen, provider);
             }
             PropertyFilter pf;
             if ((_filterId != null) && (pf = findPropertyFilter(provider, _filterId, value)) != null) {
-                serializeFilteredFields(value, gen, provider, pf, suppressableValue);
-            } else if (suppressableValue != null) {
-                serializeOptionalFields(value, gen, provider, suppressableValue);
+                serializeFilteredFields(value, gen, provider, pf, _suppressableValue);
+            } else if ((_suppressableValue != null) || _suppressNulls) {
+                serializeOptionalFields(value, gen, provider, _suppressableValue);
             } else if (_valueSerializer != null) {
                 serializeFieldsUsing(value, gen, provider, _valueSerializer);
             } else {
@@ -544,33 +647,26 @@
             TypeSerializer typeSer)
         throws IOException
     {
-        typeSer.writeTypePrefixForObject(value, gen);
         // [databind#631]: Assign current value, to be accessible by custom serializers
         gen.setCurrentValue(value);
+        WritableTypeId typeIdDef = typeSer.writeTypePrefix(gen,
+                typeSer.typeId(value, JsonToken.START_OBJECT));
         if (!value.isEmpty()) {
-            Object suppressableValue = _suppressableValue;
-            if (suppressableValue == JsonInclude.Include.ALWAYS) {
-                suppressableValue = null;
-            } else if (suppressableValue == null) {
-                if (!provider.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES)) {
-                    suppressableValue = JsonInclude.Include.NON_NULL;
-                }
-            }
             if (_sortKeys || provider.isEnabled(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)) {
-                value = _orderEntries(value, gen, provider, suppressableValue);
+                value = _orderEntries(value, gen, provider);
             }
             PropertyFilter pf;
             if ((_filterId != null) && (pf = findPropertyFilter(provider, _filterId, value)) != null) {
-                serializeFilteredFields(value, gen, provider, pf, suppressableValue);
-            } else if (suppressableValue != null) {
-                serializeOptionalFields(value, gen, provider, suppressableValue);
+                serializeFilteredFields(value, gen, provider, pf, _suppressableValue);
+            } else if ((_suppressableValue != null) || _suppressNulls) {
+                serializeOptionalFields(value, gen, provider, _suppressableValue);
             } else if (_valueSerializer != null) {
                 serializeFieldsUsing(value, gen, provider, _valueSerializer);
             } else {
                 serializeFields(value, gen, provider);
             }
         }
-        typeSer.writeTypeSuffixForObject(value, gen);
+        typeSer.writeTypeSuffix(gen, typeIdDef);
     }
 
     /*
@@ -594,48 +690,35 @@
         }
         final JsonSerializer<Object> keySerializer = _keySerializer;
         final Set<String> ignored = _ignoredEntries;
+        Object keyElem = null;
 
-        PropertySerializerMap serializers = _dynamicValueSerializers;
-
-        for (Map.Entry<?,?> entry : value.entrySet()) {
-            Object valueElem = entry.getValue();
-            // First, serialize key
-            Object keyElem = entry.getKey();
-
-            if (keyElem == null) {
-                provider.findNullKeySerializer(_keyType, _property).serialize(null, gen, provider);
-            } else {
-                // One twist: is entry ignorable? If so, skip
-                if ((ignored != null) && ignored.contains(keyElem)) continue;
-                keySerializer.serialize(keyElem, gen, provider);
-            }
-
-            // And then value
-            if (valueElem == null) {
-                provider.defaultSerializeNull(gen);
-                continue;
-            }
-            JsonSerializer<Object> serializer = _valueSerializer;
-            if (serializer == null) {
-                Class<?> cc = valueElem.getClass();
-                serializer = serializers.serializerFor(cc);
-                if (serializer == null) {
-                    if (_valueType.hasGenericTypes()) {
-                        serializer = _findAndAddDynamic(serializers,
-                                provider.constructSpecializedType(_valueType, cc), provider);
-                    } else {
-                        serializer = _findAndAddDynamic(serializers, cc, provider);
+        try {
+            for (Map.Entry<?,?> entry : value.entrySet()) {
+                Object valueElem = entry.getValue();
+                // First, serialize key
+                keyElem = entry.getKey();
+                if (keyElem == null) {
+                    provider.findNullKeySerializer(_keyType, _property).serialize(null, gen, provider);
+                } else {
+                    // One twist: is entry ignorable? If so, skip
+                    if ((ignored != null) && ignored.contains(keyElem)) {
+                        continue;
                     }
-                    serializers = _dynamicValueSerializers;
+                    keySerializer.serialize(keyElem, gen, provider);
                 }
-            }
-            try {
+                // And then value
+                if (valueElem == null) {
+                    provider.defaultSerializeNull(gen);
+                    continue;
+                }
+                JsonSerializer<Object> serializer = _valueSerializer;
+                if (serializer == null) {
+                    serializer = _findSerializer(provider, valueElem);
+                }
                 serializer.serialize(valueElem, gen, provider);
-            } catch (Exception e) {
-                // Add reference information
-                String keyDesc = ""+keyElem;
-                wrapAndThrow(provider, e, value, keyDesc);
             }
+        } catch (Exception e) { // Add reference information
+            wrapAndThrow(provider, e, value, String.valueOf(keyElem));
         }
     }
 
@@ -652,7 +735,7 @@
             return;
         }
         final Set<String> ignored = _ignoredEntries;
-        PropertySerializerMap serializers = _dynamicValueSerializers;
+        final boolean checkEmpty = (MARKER_FOR_EMPTY == suppressableValue);
 
         for (Map.Entry<?,?> entry : value.entrySet()) {
             // First find key serializer
@@ -669,29 +752,24 @@
             final Object valueElem = entry.getValue();
             JsonSerializer<Object> valueSer;
             if (valueElem == null) {
-                if (suppressableValue != null) { // all suppressions include null-suppression
+                if (_suppressNulls) { // all suppressions include null-suppression
                     continue;
                 }
                 valueSer = provider.getDefaultNullValueSerializer();
             } else {
                 valueSer = _valueSerializer;
                 if (valueSer == null) {
-                    Class<?> cc = valueElem.getClass();
-                    valueSer = serializers.serializerFor(cc);
-                    if (valueSer == null) {
-                        if (_valueType.hasGenericTypes()) {
-                            valueSer = _findAndAddDynamic(serializers,
-                                    provider.constructSpecializedType(_valueType, cc), provider);
-                        } else {
-                            valueSer = _findAndAddDynamic(serializers, cc, provider);
-                        }
-                        serializers = _dynamicValueSerializers;
-                    }
+                    valueSer = _findSerializer(provider, valueElem);
                 }
                 // also may need to skip non-empty values:
-                if ((suppressableValue == JsonInclude.Include.NON_EMPTY)
-                        && valueSer.isEmpty(provider, valueElem)) {
-                    continue;
+                if (checkEmpty) {
+                    if (valueSer.isEmpty(provider, valueElem)) {
+                        continue;
+                    }
+                } else if (suppressableValue != null) {
+                    if (suppressableValue.equals(valueElem)) {
+                        continue;
+                    }
                 }
             }
             // and then serialize, if all went well
@@ -699,8 +777,7 @@
                 keySerializer.serialize(keyElem, gen, provider);
                 valueSer.serialize(valueElem, gen, provider);
             } catch (Exception e) {
-                String keyDesc = ""+keyElem;
-                wrapAndThrow(provider, e, value, keyDesc);
+                wrapAndThrow(provider, e, value, String.valueOf(keyElem));
             }
         }
     }
@@ -738,8 +815,7 @@
                         ser.serializeWithType(valueElem, gen, provider, typeSer);
                     }
                 } catch (Exception e) {
-                    String keyDesc = ""+keyElem;
-                    wrapAndThrow(provider, e, value, keyDesc);
+                    wrapAndThrow(provider, e, value, String.valueOf(keyElem));
                 }
             }
         }
@@ -757,10 +833,9 @@
         throws IOException
     {
         final Set<String> ignored = _ignoredEntries;
-
-        PropertySerializerMap serializers = _dynamicValueSerializers;
         final MapProperty prop = new MapProperty(_valueTypeSerializer, _property);
-        
+        final boolean checkEmpty = (MARKER_FOR_EMPTY == suppressableValue);
+
         for (Map.Entry<?,?> entry : value.entrySet()) {
             // First, serialize key; unless ignorable by key
             final Object keyElem = entry.getKey();
@@ -778,49 +853,36 @@
             JsonSerializer<Object> valueSer;
             // And then value
             if (valueElem == null) {
-                if (suppressableValue != null) { // all suppressions include null-suppression
+                if (_suppressNulls) {
                     continue;
                 }
                 valueSer = provider.getDefaultNullValueSerializer();
             } else {
                 valueSer = _valueSerializer;
                 if (valueSer == null) {
-                    Class<?> cc = valueElem.getClass();
-                    valueSer = serializers.serializerFor(cc);
-                    if (valueSer == null) {
-                        if (_valueType.hasGenericTypes()) {
-                            valueSer = _findAndAddDynamic(serializers,
-                                    provider.constructSpecializedType(_valueType, cc), provider);
-                        } else {
-                            valueSer = _findAndAddDynamic(serializers, cc, provider);
-                        }
-                        serializers = _dynamicValueSerializers;
-                    }
+                    valueSer = _findSerializer(provider, valueElem);
                 }
                 // also may need to skip non-empty values:
-                if ((suppressableValue == JsonInclude.Include.NON_EMPTY)
-                        && valueSer.isEmpty(provider, valueElem)) {
-                    continue;
+                if (checkEmpty) {
+                    if (valueSer.isEmpty(provider, valueElem)) {
+                        continue;
+                    }
+                } else if (suppressableValue != null) {
+                    if (suppressableValue.equals(valueElem)) {
+                        continue;
+                    }
                 }
             }
             // and with that, ask filter to handle it
-            prop.reset(keyElem, keySerializer, valueSer);
+            prop.reset(keyElem, valueElem, keySerializer, valueSer);
             try {
-                filter.serializeAsField(valueElem, gen, provider, prop);
+                filter.serializeAsField(value, gen, provider, prop);
             } catch (Exception e) {
-                String keyDesc = ""+keyElem;
-                wrapAndThrow(provider, e, value, keyDesc);
+                wrapAndThrow(provider, e, value, String.valueOf(keyElem));
             }
         }
     }
 
-    @Deprecated // since 2.5
-    public void serializeFilteredFields(Map<?,?> value, JsonGenerator gen, SerializerProvider provider,
-            PropertyFilter filter) throws IOException {
-        serializeFilteredFields(value, gen, provider, filter,
-                provider.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES) ? null : JsonInclude.Include.NON_NULL);
-    }
-    
     /**
      * @since 2.5
      */
@@ -829,7 +891,7 @@
         throws IOException
     {
         final Set<String> ignored = _ignoredEntries;
-        PropertySerializerMap serializers = _dynamicValueSerializers;
+        final boolean checkEmpty = (MARKER_FOR_EMPTY == suppressableValue);
 
         for (Map.Entry<?,?> entry : value.entrySet()) {
             Object keyElem = entry.getKey();
@@ -846,44 +908,97 @@
             // And then value
             JsonSerializer<Object> valueSer;
             if (valueElem == null) {
-                if (suppressableValue != null) { // all suppression include null suppression
+                if (_suppressNulls) { // all suppression include null suppression
                     continue;
                 }
                 valueSer = provider.getDefaultNullValueSerializer();
             } else {
                 valueSer = _valueSerializer;
-                Class<?> cc = valueElem.getClass();
-                valueSer = serializers.serializerFor(cc);
                 if (valueSer == null) {
-                    if (_valueType.hasGenericTypes()) {
-                        valueSer = _findAndAddDynamic(serializers,
-                                provider.constructSpecializedType(_valueType, cc), provider);
-                    } else {
-                        valueSer = _findAndAddDynamic(serializers, cc, provider);
-                    }
-                    serializers = _dynamicValueSerializers;
+                    valueSer = _findSerializer(provider, valueElem);
                 }
                 // also may need to skip non-empty values:
-                if ((suppressableValue == JsonInclude.Include.NON_EMPTY)
-                        && valueSer.isEmpty(provider, valueElem)) {
-                    continue;
+                if (checkEmpty) {
+                    if (valueSer.isEmpty(provider, valueElem)) {
+                        continue;
+                    }
+                } else if (suppressableValue != null) {
+                    if (suppressableValue.equals(valueElem)) {
+                        continue;
+                    }
                 }
             }
             keySerializer.serialize(keyElem, gen, provider);
             try {
                 valueSer.serializeWithType(valueElem, gen, provider, _valueTypeSerializer);
             } catch (Exception e) {
-                String keyDesc = ""+keyElem;
-                wrapAndThrow(provider, e, value, keyDesc);
+                wrapAndThrow(provider, e, value, String.valueOf(keyElem));
             }
         }
     }
 
-    @Deprecated // since 2.5
-    protected void serializeTypedFields(Map<?,?> value, JsonGenerator gen, SerializerProvider provider)
-        throws IOException {
-        serializeTypedFields(value, gen, provider,
-                provider.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES) ? null : JsonInclude.Include.NON_NULL);
+    /**
+     * Helper method used when we have a JSON Filter to use AND contents are
+     * "any properties" of a POJO.
+     *
+     * @param bean Enclosing POJO that has any-getter used to obtain "any properties"
+     * 
+     * @since 2.9
+     */
+    public void serializeFilteredAnyProperties(SerializerProvider provider, JsonGenerator gen,
+            Object bean, Map<?,?> value, PropertyFilter filter,
+            Object suppressableValue)
+        throws IOException
+    {
+        final Set<String> ignored = _ignoredEntries;
+        final MapProperty prop = new MapProperty(_valueTypeSerializer, _property);
+        final boolean checkEmpty = (MARKER_FOR_EMPTY == suppressableValue);
+
+        for (Map.Entry<?,?> entry : value.entrySet()) {
+            // First, serialize key; unless ignorable by key
+            final Object keyElem = entry.getKey();
+            if (ignored != null && ignored.contains(keyElem)) continue;
+
+            JsonSerializer<Object> keySerializer;
+            if (keyElem == null) {
+                keySerializer = provider.findNullKeySerializer(_keyType, _property);
+            } else {
+                keySerializer = _keySerializer;
+            }
+            // or by value; nulls often suppressed
+            final Object valueElem = entry.getValue();
+
+            JsonSerializer<Object> valueSer;
+            // And then value
+            if (valueElem == null) {
+                if (_suppressNulls) {
+                    continue;
+                }
+                valueSer = provider.getDefaultNullValueSerializer();
+            } else {
+                valueSer = _valueSerializer;
+                if (valueSer == null) {
+                    valueSer = _findSerializer(provider, valueElem);
+                }
+                // also may need to skip non-empty values:
+                if (checkEmpty) {
+                    if (valueSer.isEmpty(provider, valueElem)) {
+                        continue;
+                    }
+                } else if (suppressableValue != null) {
+                    if (suppressableValue.equals(valueElem)) {
+                        continue;
+                    }
+                }
+            }
+            // and with that, ask filter to handle it
+            prop.reset(keyElem, valueElem, keySerializer, valueSer);
+            try {
+                filter.serializeAsField(bean, gen, provider, prop);
+            } catch (Exception e) {
+                wrapAndThrow(provider, e, value, String.valueOf(keyElem));
+            }
+        }
     }
 
     /*
@@ -891,11 +1006,11 @@
     /* Schema related functionality
     /**********************************************************
      */
-    
+
     @Override
     public JsonNode getSchema(SerializerProvider provider, Type typeHint)
     {
-        //(ryan) even though it's possible to statically determine the "value" type of the map,
+        // even though it's possible to statically determine the "value" type of the map,
         // there's no way to statically determine the keys, so the "Entries" can't be determined.
         return createSchemaNode("object", true);
     }
@@ -904,7 +1019,7 @@
     public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
         throws JsonMappingException
     {
-        JsonMapFormatVisitor v2 = (visitor == null) ? null : visitor.expectMapFormat(typeHint);        
+        JsonMapFormatVisitor v2 = visitor.expectMapFormat(typeHint);        
         if (v2 != null) {
             v2.keyFormat(_keySerializer, _keyType);
             JsonSerializer<?> valueSer = _valueSerializer;
@@ -921,7 +1036,7 @@
     /* Internal helper methods
     /**********************************************************
      */
-    
+
     protected final JsonSerializer<Object> _findAndAddDynamic(PropertySerializerMap map,
             Class<?> type, SerializerProvider provider) throws JsonMappingException
     {
@@ -944,7 +1059,7 @@
     }
 
     protected Map<?,?> _orderEntries(Map<?,?> input, JsonGenerator gen,
-            SerializerProvider provider, Object suppressableValue) throws IOException
+            SerializerProvider provider) throws IOException
     {
         // minor optimization: may already be sorted?
         if (input instanceof SortedMap<?,?>) {
@@ -959,7 +1074,7 @@
             for (Map.Entry<?,?> entry : input.entrySet()) {
                 Object key = entry.getKey();
                 if (key == null) {
-                    _writeNullKeyedEntry(gen, provider, suppressableValue, entry.getValue());
+                    _writeNullKeyedEntry(gen, provider, entry.getValue());
                     continue;
                 } 
                 result.put(key, entry.getValue());
@@ -986,42 +1101,50 @@
     }
     
     protected void _writeNullKeyedEntry(JsonGenerator gen, SerializerProvider provider,
-            Object suppressableValue, Object value) throws IOException
+            Object value) throws IOException
     {
         JsonSerializer<Object> keySerializer = provider.findNullKeySerializer(_keyType, _property);
         JsonSerializer<Object> valueSer;
         if (value == null) {
-            if (suppressableValue != null) { // all suppressions include null-suppression
+            if (_suppressNulls) {
                 return;
             }
             valueSer = provider.getDefaultNullValueSerializer();
         } else {
             valueSer = _valueSerializer;
             if (valueSer == null) {
-                Class<?> cc = value.getClass();
-                valueSer = _dynamicValueSerializers.serializerFor(cc);
-                if (valueSer == null) {
-                    if (_valueType.hasGenericTypes()) {
-                        valueSer = _findAndAddDynamic(_dynamicValueSerializers,
-                                provider.constructSpecializedType(_valueType, cc), provider);
-                    } else {
-                        valueSer = _findAndAddDynamic(_dynamicValueSerializers, cc, provider);
-                    }
-                }
+                valueSer = _findSerializer(provider, value);
             }
-            // also may need to skip non-empty values:
-            if ((suppressableValue == JsonInclude.Include.NON_EMPTY)
-                    && valueSer.isEmpty(provider, value)) {
+            if (_suppressableValue == MARKER_FOR_EMPTY) {
+                if (valueSer.isEmpty(provider, value)) {
+                    return;
+                }
+            } else if ((_suppressableValue != null)
+                && (_suppressableValue.equals(value))) {
                 return;
             }
         }
-        // and then serialize, if all went well
+
         try {
             keySerializer.serialize(null, gen, provider);
             valueSer.serialize(value, gen, provider);
         } catch (Exception e) {
-            String keyDesc = "";
-            wrapAndThrow(provider, e, value, keyDesc);
+            wrapAndThrow(provider, e, value, "");
         }
     }
+
+    private final JsonSerializer<Object> _findSerializer(SerializerProvider provider,
+            Object value) throws JsonMappingException
+    {
+        final Class<?> cc = value.getClass();
+        JsonSerializer<Object> valueSer = _dynamicValueSerializers.serializerFor(cc);
+        if (valueSer != null) {
+            return valueSer;
+        }
+        if (_valueType.hasGenericTypes()) {
+            return _findAndAddDynamic(_dynamicValueSerializers,
+                    provider.constructSpecializedType(_valueType, cc), provider);
+        }
+        return _findAndAddDynamic(_dynamicValueSerializers, cc, provider);
+    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/NonTypedScalarSerializerBase.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/NonTypedScalarSerializerBase.java
index a0f75a3..04b1cd5 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/NonTypedScalarSerializerBase.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/NonTypedScalarSerializerBase.java
@@ -14,6 +14,7 @@
  * {@link java.lang.Double} and {@link java.lang.Boolean}.
  */
 @SuppressWarnings("serial")
+@Deprecated // since 2.9
 public abstract class NonTypedScalarSerializerBase<T>
     extends StdScalarSerializer<T>
 {
@@ -24,7 +25,7 @@
     protected NonTypedScalarSerializerBase(Class<?> t, boolean bogus) {
         super(t, bogus);
     }
-    
+
     @Override
     public final void serializeWithType(T value, JsonGenerator gen, SerializerProvider provider,
             TypeSerializer typeSer) throws IOException
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/NullSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/NullSerializer.java
index 2171644..e7e8a2f 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/NullSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/NullSerializer.java
@@ -34,7 +34,7 @@
      * Although this method should rarely get called, for convenience we should override
      * it, and handle it same way as "natural" types: by serializing exactly as is,
      * without type decorations. The most common possible use case is that of delegation
-     * by JSON filter; caller can not know what kind of serializer it gets handed.
+     * by JSON filter; caller cannot know what kind of serializer it gets handed.
      */
     @Override
     public void serializeWithType(Object value, JsonGenerator gen, SerializerProvider serializers,
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializer.java
index 4e57a2a..1180798 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializer.java
@@ -5,11 +5,13 @@
 import java.math.BigDecimal;
 import java.math.BigInteger;
 
+import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.core.JsonParser;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
+import com.fasterxml.jackson.databind.ser.ContextualSerializer;
 
 /**
  * As a fallback, we may need to use this serializer for other
@@ -20,6 +22,7 @@
 @SuppressWarnings("serial")
 public class NumberSerializer
     extends StdScalarSerializer<Number>
+    implements ContextualSerializer
 {
     /**
      * Static instance that is only to be used for {@link java.lang.Number}.
@@ -38,6 +41,21 @@
     }
 
     @Override
+    public JsonSerializer<?> createContextual(SerializerProvider prov,
+            BeanProperty property) throws JsonMappingException
+    {
+        JsonFormat.Value format = findFormatOverrides(prov, property, handledType());
+        if (format != null) {
+            switch (format.getShape()) {
+            case STRING:
+                return ToStringSerializer.instance;
+            default:
+            }
+        }
+        return this;
+    }
+
+    @Override
     public void serialize(Number value, JsonGenerator g, SerializerProvider provider) throws IOException
     {
         // should mostly come in as one of these two:
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/ObjectArraySerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/ObjectArraySerializer.java
index d71bc40..36dca04 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/ObjectArraySerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/ObjectArraySerializer.java
@@ -1,24 +1,20 @@
 package com.fasterxml.jackson.databind.ser.std;
 
 import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Type;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
+
 import com.fasterxml.jackson.core.*;
+
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
 import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonArrayFormatVisitor;
 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
-import com.fasterxml.jackson.databind.jsonschema.SchemaAware;
 import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
-import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.fasterxml.jackson.databind.ser.ContainerSerializer;
 import com.fasterxml.jackson.databind.ser.ContextualSerializer;
 import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap;
-import com.fasterxml.jackson.databind.type.ArrayType;
-import com.fasterxml.jackson.databind.type.TypeFactory;
 
 /**
  * Generic serializer for Object arrays (<code>Object[]</code>).
@@ -51,7 +47,7 @@
     protected JsonSerializer<Object> _elementSerializer;
 
     /**
-     * If element type can not be statically determined, mapping from
+     * If element type cannot be statically determined, mapping from
      * runtime type to serializer is handled using this object
      */
     protected PropertySerializerMap _dynamicSerializers;
@@ -153,7 +149,7 @@
             ser = _elementSerializer;
         }
         // [databind#124]: May have a content converter
-        ser = findConvertingContentSerializer(serializers, property, ser);
+        ser = findContextualConvertingSerializer(serializers, property, ser);
         if (ser == null) {
             // 30-Sep-2012, tatu: One more thing -- if explicit content type is annotated,
             //   we can consider it a static case as well.
@@ -162,8 +158,6 @@
                     ser = serializers.findValueSerializer(_elementType, property);
                 }
             }
-        } else {
-            ser = serializers.handleSecondaryContextualization(ser, property);
         }
         return withResolved(property, vts, ser, unwrapSingle);
     }
@@ -186,7 +180,7 @@
 
     @Override
     public boolean isEmpty(SerializerProvider prov, Object[] value) {
-        return (value == null) || (value.length == 0);
+        return value.length == 0;
     }
 
     @Override
@@ -245,7 +239,6 @@
                 Class<?> cc = elem.getClass();
                 JsonSerializer<Object> serializer = serializers.serializerFor(cc);
                 if (serializer == null) {
-                    // To fix [JACKSON-508]
                     if (_elementType.hasGenericTypes()) {
                         serializer = _findAndAddDynamic(serializers,
                                 provider.constructSpecializedType(_elementType, cc), provider);
@@ -255,22 +248,8 @@
                 }
                 serializer.serialize(elem, gen, provider);
             }
-        } catch (IOException ioe) {
-            throw ioe;
         } catch (Exception e) {
-            // [JACKSON-55] Need to add reference information
-            /* 05-Mar-2009, tatu: But one nasty edge is when we get
-             *   StackOverflow: usually due to infinite loop. But that gets
-             *   hidden within an InvocationTargetException...
-             */
-            Throwable t = e;
-            while (t instanceof InvocationTargetException && t.getCause() != null) {
-                t = t.getCause();
-            }
-            if (t instanceof Error) {
-                throw (Error) t;
-            }
-            throw JsonMappingException.wrapWithPath(t, elem, i);
+            wrapAndThrow(provider, e, elem, i);
         }
     }
 
@@ -295,17 +274,8 @@
                     ser.serializeWithType(elem, jgen, provider, typeSer);
                 }
             }
-        } catch (IOException ioe) {
-            throw ioe;
         } catch (Exception e) {
-            Throwable t = e;
-            while (t instanceof InvocationTargetException && t.getCause() != null) {
-                t = t.getCause();
-            }
-            if (t instanceof Error) {
-                throw (Error) t;
-            }
-            throw JsonMappingException.wrapWithPath(t, elem, i);
+            wrapAndThrow(provider, e, elem, i);
         }
     }
 
@@ -330,44 +300,10 @@
                 }
                 serializer.serializeWithType(elem, jgen, provider, typeSer);
             }
-        } catch (IOException ioe) {
-            throw ioe;
         } catch (Exception e) {
-            Throwable t = e;
-            while (t instanceof InvocationTargetException && t.getCause() != null) {
-                t = t.getCause();
-            }
-            if (t instanceof Error) {
-                throw (Error) t;
-            }
-            throw JsonMappingException.wrapWithPath(t, elem, i);
+            wrapAndThrow(provider, e, elem, i);
         }
     }
-    
-    @SuppressWarnings("deprecation")
-    @Override
-    public JsonNode getSchema(SerializerProvider provider, Type typeHint)
-        throws JsonMappingException
-    {
-        ObjectNode o = createSchemaNode("array", true);
-        if (typeHint != null) {
-            JavaType javaType = provider.constructType(typeHint);
-            if (javaType.isArrayType()) {
-                Class<?> componentType = ((ArrayType) javaType).getContentType().getRawClass();
-                // 15-Oct-2010, tatu: We can't serialize plain Object.class; but what should it produce here? Untyped?
-                if (componentType == Object.class) {
-                    o.set("items", com.fasterxml.jackson.databind.jsonschema.JsonSchema.getDefaultSchemaNode());
-                } else {
-                    JsonSerializer<Object> ser = provider.findValueSerializer(componentType, _property);
-                    JsonNode schemaNode = (ser instanceof SchemaAware) ?
-                            ((SchemaAware) ser).getSchema(provider, null) :
-                            	com.fasterxml.jackson.databind.jsonschema.JsonSchema.getDefaultSchemaNode();
-                    o.set("items", schemaNode);
-                }
-            }
-        }
-        return o;
-    }
 
     @Override
     public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
@@ -375,11 +311,17 @@
     {
         JsonArrayFormatVisitor arrayVisitor = visitor.expectArrayFormat(typeHint);
         if (arrayVisitor != null) {
+            JavaType contentType = _elementType;
+
+            // [databind#1793]: Was getting `null` for `typeHint`. But why would we even use it...
+/*
             TypeFactory tf = visitor.getProvider().getTypeFactory();
-            JavaType contentType = tf.moreSpecificType(_elementType, typeHint.getContentType());
+            contentType = tf.moreSpecificType(_elementType, typeHint.getContentType());
             if (contentType == null) {
-                throw JsonMappingException.from(visitor.getProvider(), "Could not resolve type");
+                visitor.getProvider().reportBadDefinition(_elementType,
+                        "Could not resolve type: "+_elementType);
             }
+*/
             JsonSerializer<?> valueSer = _elementSerializer;
             if (valueSer == null) {
                 valueSer = visitor.getProvider().findValueSerializer(contentType, _property);
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/RawSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/RawSerializer.java
index d6ae1da..bed8e7a 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/RawSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/RawSerializer.java
@@ -4,6 +4,7 @@
 import java.io.IOException;
 
 import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.type.WritableTypeId;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
 import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
@@ -18,7 +19,7 @@
 {
     /**
      * Constructor takes in expected type of values; but since caller
-     * typically can not really provide actual type parameter, we will
+     * typically cannot really provide actual type parameter, we will
      * just take wild card and coerce type.
      */
     public RawSerializer(Class<?> cls) {
@@ -31,13 +32,14 @@
     }
 
     @Override
-    public void serializeWithType(T value, JsonGenerator jgen, SerializerProvider provider,
+    public void serializeWithType(T value, JsonGenerator g, SerializerProvider provider,
             TypeSerializer typeSer)
         throws IOException
     {
-        typeSer.writeTypePrefixForScalar(value, jgen);
-        serialize(value, jgen, provider);
-        typeSer.writeTypeSuffixForScalar(value, jgen);
+        WritableTypeId typeIdDef = typeSer.writeTypePrefix(g,
+                typeSer.typeId(value, JsonToken.VALUE_EMBEDDED_OBJECT));
+        serialize(value, g, provider);
+        typeSer.writeTypeSuffix(g, typeIdDef);
     }
     
     @Override
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/ReferenceTypeSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/ReferenceTypeSerializer.java
index 095d209..b300c35 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/ReferenceTypeSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/ReferenceTypeSerializer.java
@@ -12,6 +12,8 @@
 import com.fasterxml.jackson.databind.ser.ContextualSerializer;
 import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap;
 import com.fasterxml.jackson.databind.type.ReferenceType;
+import com.fasterxml.jackson.databind.util.ArrayBuilders;
+import com.fasterxml.jackson.databind.util.BeanUtil;
 import com.fasterxml.jackson.databind.util.NameTransformer;
 
 /**
@@ -28,6 +30,11 @@
     private static final long serialVersionUID = 1L;
 
     /**
+     * @since 2.9
+     */
+    public final static Object MARKER_FOR_EMPTY = JsonInclude.Include.NON_EMPTY;
+    
+    /**
      * Value type
      */
     protected final JavaType _referredType;
@@ -50,19 +57,39 @@
     protected final NameTransformer _unwrapper;
 
     /**
-     * Further guidance on serialization-inclusion (or not), regarding
-     * contained value (if any).
-     */
-    protected final JsonInclude.Include _contentInclusion;
-    
-    /**
-     * If element type can not be statically determined, mapping from
+     * If element type cannot be statically determined, mapping from
      * runtime type to serializer is handled using this object
      */
     protected transient PropertySerializerMap _dynamicSerializers;
 
     /*
     /**********************************************************
+    /* Config settings, filtering
+    /**********************************************************
+     */
+
+    /**
+     * Value that indicates suppression mechanism to use for <b>values contained</b>;
+     * either "filter" (of which <code>equals()</code> is called), or marker
+     * value of {@link #MARKER_FOR_EMPTY}, or null to indicate no filtering for
+     * non-null values.
+     * Note that inclusion value for Map instance itself is handled by caller (POJO
+     * property that refers to the Map value).
+     *
+     * @since 2.9
+     */
+    protected final Object _suppressableValue;
+
+    /**
+     * Flag that indicates what to do with `null` values, distinct from
+     * handling of {@link #_suppressableValue}
+     *
+     * @since 2.9
+     */
+    protected final boolean _suppressNulls;
+
+    /*
+    /**********************************************************
     /* Constructors, factory methods
     /**********************************************************
      */
@@ -76,7 +103,8 @@
         _valueTypeSerializer = vts;
         _valueSerializer = ser;
         _unwrapper = null;
-        _contentInclusion = null;
+        _suppressableValue = null;
+        _suppressNulls = false;
         _dynamicSerializers = PropertySerializerMap.emptyForProperties();
     }
 
@@ -84,7 +112,7 @@
     protected ReferenceTypeSerializer(ReferenceTypeSerializer<?> base, BeanProperty property,
             TypeSerializer vts, JsonSerializer<?> valueSer,
             NameTransformer unwrapper,
-            JsonInclude.Include contentIncl)
+            Object suppressableValue, boolean suppressNulls)
     {
         super(base);
         _referredType = base._referredType;
@@ -93,23 +121,22 @@
         _valueTypeSerializer = vts;
         _valueSerializer = (JsonSerializer<Object>) valueSer;
         _unwrapper = unwrapper;
-        if ((contentIncl == JsonInclude.Include.USE_DEFAULTS)
-                || (contentIncl == JsonInclude.Include.ALWAYS)) {
-            _contentInclusion = null;
-        } else {
-            _contentInclusion = contentIncl;
-        }
+        _suppressableValue = suppressableValue;
+        _suppressNulls = suppressNulls;
     }
 
     @Override
     public JsonSerializer<T> unwrappingSerializer(NameTransformer transformer) {
-        JsonSerializer<Object> ser = _valueSerializer;
-        if (ser != null) {
-            ser = ser.unwrappingSerializer(transformer);
+        JsonSerializer<Object> valueSer = _valueSerializer;
+        if (valueSer != null) {
+            valueSer = valueSer.unwrappingSerializer(transformer);
         }
         NameTransformer unwrapper = (_unwrapper == null) ? transformer
                 : NameTransformer.chainedTransformer(transformer, _unwrapper);
-        return withResolved(_property, _valueTypeSerializer, ser, unwrapper, _contentInclusion);
+        if ((_valueSerializer == valueSer) && (_unwrapper == unwrapper)) {
+            return this;
+        }
+        return withResolved(_property, _valueTypeSerializer, valueSer, unwrapper);
     }
 
     /*
@@ -117,13 +144,39 @@
     /* Abstract methods to implement
     /**********************************************************
      */
-    
+
+    /**
+     * Mutant factory method called when changes are needed; should construct
+     * newly configured instance with new values as indicated.
+     *<p>
+     * NOTE: caller has verified that there are changes, so implementations
+     * need NOT check if a new instance is needed.
+     *
+     * @since 2.9
+     */
     protected abstract ReferenceTypeSerializer<T> withResolved(BeanProperty prop,
             TypeSerializer vts, JsonSerializer<?> valueSer,
-            NameTransformer unwrapper,
-            JsonInclude.Include contentIncl);
+            NameTransformer unwrapper);
 
-    protected abstract boolean _isValueEmpty(T value);
+    /**
+     * Mutant factory method called to create a differently constructed instance,
+     * specifically with different exclusion rules for contained value.
+     *<p>
+     * NOTE: caller has verified that there are changes, so implementations
+     * need NOT check if a new instance is needed.
+     *
+     * @since 2.9
+     */
+    public abstract ReferenceTypeSerializer<T> withContentInclusion(Object suppressableValue,
+            boolean suppressNulls);
+
+    /**
+     * Method called to see if there is a value present or not.
+     * Note that value itself may still be `null`, even if present,
+     * if referential type allows three states (absent, present-null,
+     * present-non-null); some only allow two (absent, present-non-null).
+     */
+    protected abstract boolean _isValuePresent(T value);
 
     protected abstract Object _getReferenced(T value);
 
@@ -144,7 +197,7 @@
             typeSer = typeSer.forProperty(property);
         }
         // First: do we have an annotation override from property?
-        JsonSerializer<?> ser = findAnnotatedContentSerializer(provider, property);;
+        JsonSerializer<?> ser = findAnnotatedContentSerializer(provider, property);
         if (ser == null) {
             // If not, use whatever was configured by type
             ser = _valueSerializer;
@@ -157,14 +210,68 @@
                 ser = provider.handlePrimaryContextualization(ser, property);
             }
         }
-        // Also: may want to have more refined exclusion based on referenced value
-        JsonInclude.Include contentIncl = _contentInclusion;
-        JsonInclude.Value incl = findIncludeOverrides(provider, property, handledType());
-        JsonInclude.Include newIncl = incl.getContentInclusion();
-        if ((newIncl != contentIncl) && (newIncl != JsonInclude.Include.USE_DEFAULTS)) {
-            contentIncl = newIncl;
+        // First, resolve wrt property, resolved serializers
+        ReferenceTypeSerializer<?> refSer;
+        if ((_property == property)
+                && (_valueTypeSerializer == typeSer) && (_valueSerializer == ser)) {
+            refSer = this;
+        } else {
+            refSer = withResolved(property, typeSer, ser, _unwrapper);
         }
-        return withResolved(property, typeSer, ser, _unwrapper, contentIncl);
+
+        // and then see if we have property-inclusion overrides
+        if (property != null) {
+            JsonInclude.Value inclV = property.findPropertyInclusion(provider.getConfig(), handledType());
+            if (inclV != null) {
+                JsonInclude.Include incl = inclV.getContentInclusion();
+
+                if (incl != JsonInclude.Include.USE_DEFAULTS) {
+                    Object valueToSuppress;
+                    boolean suppressNulls;
+                    switch (incl) {
+                    case NON_DEFAULT:
+                        valueToSuppress = BeanUtil.getDefaultValue(_referredType);
+                        suppressNulls = true;
+                        if (valueToSuppress != null) {
+                            if (valueToSuppress.getClass().isArray()) {
+                                valueToSuppress = ArrayBuilders.getArrayComparator(valueToSuppress);
+                            }
+                        }
+                        break;
+                    case NON_ABSENT:
+                        suppressNulls = true;
+                        valueToSuppress = _referredType.isReferenceType() ? MARKER_FOR_EMPTY : null;
+                        break;
+                    case NON_EMPTY:
+                        suppressNulls = true;
+                        valueToSuppress = MARKER_FOR_EMPTY;
+                        break;
+                    case CUSTOM:
+                        valueToSuppress = provider.includeFilterInstance(null, inclV.getContentFilter());
+                        if (valueToSuppress == null) { // is this legal?
+                            suppressNulls = true;
+                        } else {
+                            suppressNulls = provider.includeFilterSuppressNulls(valueToSuppress);
+                        }
+                        break;
+                    case NON_NULL:
+                        valueToSuppress = null;
+                        suppressNulls = true;
+                        break;
+                    case ALWAYS: // default
+                    default:
+                        valueToSuppress = null;
+                        suppressNulls = false;
+                        break;
+                    }
+                    if ((_suppressableValue != valueToSuppress)
+                            || (_suppressNulls != suppressNulls)) {
+                        refSer = refSer.withContentInclusion(valueToSuppress, suppressNulls);
+                    }
+                }
+            }
+        }
+        return refSer;
     }
 
     protected boolean _useStatic(SerializerProvider provider, BeanProperty property,
@@ -209,13 +316,17 @@
     @Override
     public boolean isEmpty(SerializerProvider provider, T value)
     {
-        if ((value == null) || _isValueEmpty(value)) {
+        // First, absent value (note: null check is just sanity check here)
+        if (!_isValuePresent(value)) {
             return true;
         }
-        if (_contentInclusion == null) {
+        Object contents = _getReferenced(value);
+        if (contents == null) { // possible for explicitly contained `null`
+            return _suppressNulls;
+        }
+        if (_suppressableValue == null) {
             return false;
         }
-        Object contents = _getReferenced(value);
         JsonSerializer<Object> ser = _valueSerializer;
         if (ser == null) {
             try {
@@ -224,7 +335,10 @@
                 throw new RuntimeJsonMappingException(e);
             }
         }
-        return ser.isEmpty(provider, contents);
+        if (_suppressableValue == MARKER_FOR_EMPTY) {
+            return ser.isEmpty(provider, contents);
+        }
+        return _suppressableValue.equals(contents);
     }
 
     @Override
@@ -232,6 +346,13 @@
         return (_unwrapper != null);
     }
 
+    /**
+     * @since 2.9
+     */
+    public JavaType getReferredType() {
+        return _referredType;
+    }
+
     /*
     /**********************************************************
     /* Serialization methods
@@ -321,33 +442,36 @@
      * serializer.
      */
     private final JsonSerializer<Object> _findCachedSerializer(SerializerProvider provider,
-            Class<?> type) throws JsonMappingException
+            Class<?> rawType) throws JsonMappingException
     {
-        JsonSerializer<Object> ser = _dynamicSerializers.serializerFor(type);
+        JsonSerializer<Object> ser = _dynamicSerializers.serializerFor(rawType);
         if (ser == null) {
-            ser = _findSerializer(provider, type, _property);
+            // NOTE: call this instead of `map._findAndAddDynamic(...)` (which in turn calls
+            // `findAndAddSecondarySerializer`) since we may need to apply unwrapper
+            // too, before caching. But calls made are the same
+            if (_referredType.hasGenericTypes()) {
+                // [databind#1673] Must ensure we will resolve all available type information
+                //  so as not to miss generic declaration of, say, `List<GenericPojo>`...
+                JavaType fullType = provider.constructSpecializedType(_referredType, rawType);
+                ser = provider.findValueSerializer(fullType, _property);
+            } else {
+                ser = provider.findValueSerializer(rawType, _property);
+            }
             if (_unwrapper != null) {
                 ser = ser.unwrappingSerializer(_unwrapper);
             }
-            _dynamicSerializers = _dynamicSerializers.newWith(type, ser);
+            _dynamicSerializers = _dynamicSerializers.newWith(rawType, ser);
         }
         return ser;
     }
 
     private final JsonSerializer<Object> _findSerializer(SerializerProvider provider,
-            Class<?> type, BeanProperty prop) throws JsonMappingException
-    {
-        // 13-Mar-2017, tatu: Used to call `findTypeValueSerializer()`, but contextualization
-        //   not working for that case for some reason
- //        return provider.findTypedValueSerializer(type, true, prop);
-        return provider.findValueSerializer(type, prop);
-    }
-
-    private final JsonSerializer<Object> _findSerializer(SerializerProvider provider,
         JavaType type, BeanProperty prop) throws JsonMappingException
     {
         // 13-Mar-2017, tatu: Used to call `findTypeValueSerializer()`, but contextualization
         //   not working for that case for some reason
+        // 15-Jan-2017, tatu: ... possibly because we need to access "secondary" serializer,
+        //   not primary (primary being one for Reference type itself, not value)
 //        return provider.findTypedValueSerializer(type, true, prop);
         return provider.findValueSerializer(type, prop);
     }
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/SerializableSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/SerializableSerializer.java
index 793a49c..93efb68 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/SerializableSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/SerializableSerializer.java
@@ -1,28 +1,21 @@
 package com.fasterxml.jackson.databind.ser.std;
 
 import java.io.IOException;
-import java.lang.reflect.Type;
-import java.util.concurrent.atomic.AtomicReference;
 
 import com.fasterxml.jackson.core.*;
 import com.fasterxml.jackson.databind.JavaType;
 import com.fasterxml.jackson.databind.JsonMappingException;
-import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.JsonSerializable;
-import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.SerializerProvider;
 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
-import com.fasterxml.jackson.databind.jsonschema.JsonSerializableSchema;
 import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import com.fasterxml.jackson.databind.type.TypeFactory;
 
 /**
  * Generic handler for types that implement {@link JsonSerializable}.
  *<p>
  * Note: given that this is used for anything that implements
- * interface, can not be checked for direct class equivalence.
+ * interface, cannot be checked for direct class equivalence.
  */
 @JacksonStdImpl
 @SuppressWarnings("serial")
@@ -31,9 +24,6 @@
 {
     public final static SerializableSerializer instance = new SerializableSerializer();
 
-    // Ugh. Should NOT need this...
-    private final static AtomicReference<ObjectMapper> _mapperReference = new AtomicReference<ObjectMapper>();
-    
     protected SerializableSerializer() { super(JsonSerializable.class); }
 
     @Override
@@ -54,61 +44,6 @@
             TypeSerializer typeSer) throws IOException {
         value.serializeWithType(gen, serializers, typeSer);
     }
-    
-    @Override
-    @SuppressWarnings("deprecation")
-    public JsonNode getSchema(SerializerProvider provider, Type typeHint)
-        throws JsonMappingException
-    {
-        ObjectNode objectNode = createObjectNode();
-        String schemaType = "any";
-        String objectProperties = null;
-        String itemDefinition = null;
-        if (typeHint != null) {
-            Class<?> rawClass = TypeFactory.rawClass(typeHint);
-            if (rawClass.isAnnotationPresent(JsonSerializableSchema.class)) {
-                JsonSerializableSchema schemaInfo = rawClass.getAnnotation(JsonSerializableSchema.class);
-                schemaType = schemaInfo.schemaType();
-                if (!JsonSerializableSchema.NO_VALUE.equals(schemaInfo.schemaObjectPropertiesDefinition())) {
-                    objectProperties = schemaInfo.schemaObjectPropertiesDefinition();
-                }
-                if (!JsonSerializableSchema.NO_VALUE.equals(schemaInfo.schemaItemDefinition())) {
-                    itemDefinition = schemaInfo.schemaItemDefinition();
-                }
-            }
-        }
-        /* 19-Mar-2012, tatu: geez, this is butt-ugly abonimation of code...
-         *    really, really should not require back ref to an ObjectMapper.
-         */
-        objectNode.put("type", schemaType);
-        if (objectProperties != null) {
-            try {
-                objectNode.set("properties", _getObjectMapper().readTree(objectProperties));
-            } catch (IOException e) {
-                provider.reportMappingProblem("Failed to parse @JsonSerializableSchema.schemaObjectPropertiesDefinition value");
-            }
-        }
-        if (itemDefinition != null) {
-            try {
-                objectNode.set("items", _getObjectMapper().readTree(itemDefinition));
-            } catch (IOException e) {
-                provider.reportMappingProblem("Failed to parse @JsonSerializableSchema.schemaItemDefinition value");
-            }
-        }
-        // always optional, no need to specify:
-        //objectNode.put("required", false);
-        return objectNode;
-    }
-    
-    private final static synchronized ObjectMapper _getObjectMapper()
-    {
-        ObjectMapper mapper = _mapperReference.get();
-        if (mapper == null) {
-            mapper = new ObjectMapper();
-            _mapperReference.set(mapper);
-        }
-        return mapper;
-    }
 
     @Override
     public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/SqlDateSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/SqlDateSerializer.java
index 2857b6f..c3cf092 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/SqlDateSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/SqlDateSerializer.java
@@ -1,14 +1,12 @@
 package com.fasterxml.jackson.databind.ser.std;
 
 import java.io.IOException;
-import java.lang.reflect.Type;
 import java.text.DateFormat;
 
-import com.fasterxml.jackson.core.JsonGenerationException;
 import com.fasterxml.jackson.core.JsonGenerator;
+
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
-import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
 
 /**
  * Compared to regular {@link java.util.Date} serialization, we do use String
@@ -21,19 +19,17 @@
     extends DateTimeSerializerBase<java.sql.Date>
 {
     public SqlDateSerializer() {
-        /* 12-Apr-2014, tatu: for now, pass explicit 'false' to mean 'not using timestamp',
-         *     for backwards compatibility; this differs from other Date/Calendar types.
-         */
-        this(Boolean.FALSE);
+        // 11-Oct-2016, tatu: As per [databind#219] fixed for 2.9; was passing `false` prior
+        this(null, null);
     }
 
-    protected SqlDateSerializer(Boolean useTimestamp) {
-        super(java.sql.Date.class, useTimestamp, null);
+    protected SqlDateSerializer(Boolean useTimestamp, DateFormat customFormat) {
+        super(java.sql.Date.class, useTimestamp, customFormat);
     }
 
     @Override
     public SqlDateSerializer withFormat(Boolean timestamp, DateFormat customFormat) {
-    	return new SqlDateSerializer(timestamp);
+    	return new SqlDateSerializer(timestamp, customFormat);
     }
 
     @Override
@@ -42,26 +38,21 @@
     }
     
     @Override
-    public void serialize(java.sql.Date value, JsonGenerator gen, SerializerProvider provider)
-        throws IOException, JsonGenerationException
+    public void serialize(java.sql.Date value, JsonGenerator g, SerializerProvider provider)
+        throws IOException
     {
         if (_asTimestamp(provider)) {
-            gen.writeNumber(_timestamp(value));
-        } else {
-            gen.writeString(value.toString());
+            g.writeNumber(_timestamp(value));
+            return;
         }
-    }
-
-    @Override
-    public JsonNode getSchema(SerializerProvider provider, Type typeHint)
-    {
-        //todo: (ryan) add a format for the date in the schema?
-        return createSchemaNode("string", true);
-    }
-    
-    @Override
-    public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException
-    {
-        _acceptJsonFormatVisitor(visitor, typeHint, _useTimestamp);
+        // Alas, can't just call `_serializeAsString()`....
+        if (_customFormat == null) {
+            // 11-Oct-2016, tatu: For backwards-compatibility purposes, we shall just use
+            //    the awful standard JDK serialization via `sqlDate.toString()`... this
+            //    is problematic in multiple ways (including using arbitrary timezone...)
+            g.writeString(value.toString());
+            return;
+        }
+        _serializeAsString(value, g, provider);
     }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/StaticListSerializerBase.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/StaticListSerializerBase.java
index 759c46d..84ac2ba 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/StaticListSerializerBase.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StaticListSerializerBase.java
@@ -1,13 +1,16 @@
 package com.fasterxml.jackson.databind.ser.std;
 
+import java.io.IOException;
 import java.lang.reflect.Type;
 import java.util.*;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonArrayFormatVisitor;
 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
 import com.fasterxml.jackson.databind.ser.ContextualSerializer;
 
 /**
@@ -19,8 +22,6 @@
     extends StdSerializer<T>
     implements ContextualSerializer
 {
-    protected final JsonSerializer<String> _serializer;
-
     /**
      * Setting for specific local override for "unwrap single element arrays":
      * true for enable unwrapping, false for preventing it, `null` for using
@@ -29,29 +30,26 @@
      * @since 2.6
      */
     protected final Boolean _unwrapSingle;
-    
+
     protected StaticListSerializerBase(Class<?> cls) {
         super(cls, false);
-        _serializer = null;
         _unwrapSingle = null;
     }
 
     /**
-     * @since 2.6
+     * @since 2.9
      */
-    @SuppressWarnings("unchecked")
     protected StaticListSerializerBase(StaticListSerializerBase<?> src,
-            JsonSerializer<?> ser, Boolean unwrapSingle) {
+            Boolean unwrapSingle) {
         super(src);
-        _serializer = (JsonSerializer<String>) ser;
         _unwrapSingle = unwrapSingle;
     }
 
     /**
-     * @since 2.6
+     * @since 2.9
      */
     public abstract JsonSerializer<?> _withResolved(BeanProperty prop,
-            JsonSerializer<?> ser, Boolean unwrapSingle);
+            Boolean unwrapSingle);
 
     /*
     /**********************************************************
@@ -59,6 +57,7 @@
     /**********************************************************
      */
 
+    @SuppressWarnings("unchecked")
     @Override
     public JsonSerializer<?> createContextual(SerializerProvider serializers,
             BeanProperty property)
@@ -81,31 +80,22 @@
         if (format != null) {
             unwrapSingle = format.getFeature(JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED);
         }
-        if (ser == null) {
-            ser = _serializer;
-        }
         // [databind#124]: May have a content converter
-        ser = findConvertingContentSerializer(serializers, property, ser);
+        ser = findContextualConvertingSerializer(serializers, property, ser);
         if (ser == null) {
             ser = serializers.findValueSerializer(String.class, property);
-        } else {
-            ser = serializers.handleSecondaryContextualization(ser, property);
         }
         // Optimization: default serializer just writes String, so we can avoid a call:
         if (isDefaultSerializer(ser)) {
-            ser = null;
+            if (unwrapSingle == _unwrapSingle) {
+                return this;
+            }
+            return _withResolved(property, unwrapSingle);
         }
+        // otherwise...
         // note: will never have TypeSerializer, because Strings are "natural" type
-        if ((ser == _serializer) && (unwrapSingle == _unwrapSingle)) {
-            return this;
-        }
-        return _withResolved(property, ser, unwrapSingle);
-    }
-    
-    @Deprecated // since 2.5
-    @Override
-    public boolean isEmpty(T value) {
-        return isEmpty(null, value);
+        return new CollectionSerializer(serializers.constructType(String.class),
+                true, /*TypeSerializer*/ null, (JsonSerializer<Object>) ser);
     }
 
     @Override
@@ -117,7 +107,7 @@
     public JsonNode getSchema(SerializerProvider provider, Type typeHint) {
         return createSchemaNode("array", true).set("items", contentSchema());
     }
-    
+
     @Override
     public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException {
         acceptContentVisitor(visitor.expectArrayFormat(typeHint));
@@ -130,7 +120,12 @@
      */
 
     protected abstract JsonNode contentSchema();
-    
+
     protected abstract void acceptContentVisitor(JsonArrayFormatVisitor visitor)
         throws JsonMappingException;
+
+    // just to make sure it gets implemented:
+    @Override
+    public abstract void serializeWithType(T value, JsonGenerator g,
+            SerializerProvider provider, TypeSerializer typeSer) throws IOException;
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdArraySerializers.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdArraySerializers.java
index 16c7e96..eb05fa4 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdArraySerializers.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdArraySerializers.java
@@ -5,6 +5,7 @@
 import java.util.HashMap;
 
 import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.type.WritableTypeId;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
@@ -58,23 +59,24 @@
     protected abstract static class TypedPrimitiveArraySerializer<T>
         extends ArraySerializerBase<T>
     {
-        /**
-         * Type serializer to use for values, if any.
-         */
-        protected final TypeSerializer _valueTypeSerializer;
-        
         protected TypedPrimitiveArraySerializer(Class<T> cls) {
             super(cls);
-            _valueTypeSerializer = null;
         }
 
         protected TypedPrimitiveArraySerializer(TypedPrimitiveArraySerializer<T> src,
-                BeanProperty prop, TypeSerializer vts, Boolean unwrapSingle) {
+                BeanProperty prop, Boolean unwrapSingle) {
             super(src, prop, unwrapSingle);
-            _valueTypeSerializer = vts;
+        }
+
+        // 01-Dec-2016, tatu: Only now realized that due strong typing of Java arrays,
+        //    we cannot really ever have value type serializers
+        @Override
+        public final ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer vts) {
+            // throw exception or just do nothing?
+            return this;
         }
     }
-    
+
     /*
     /****************************************************************
     /* Concrete serializers, arrays
@@ -123,7 +125,7 @@
         
         @Override
         public boolean isEmpty(SerializerProvider prov, boolean[] value) {
-            return (value == null) || (value.length == 0);
+            return value.length == 0;
         }
 
         @Override
@@ -135,22 +137,19 @@
         public final void serialize(boolean[] value, JsonGenerator g, SerializerProvider provider) throws IOException
         {
             final int len = value.length;
-            if (len == 1) {
-                if (((_unwrapSingle == null) &&
-                        provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED))
-                        || (_unwrapSingle == Boolean.TRUE)) {
-                    serializeContents(value, g, provider);
-                    return;
-                }
+            if ((len == 1) && _shouldUnwrapSingle(provider)) {
+                serializeContents(value, g, provider);
+                return;
             }
             g.writeStartArray(len);
+            g.setCurrentValue(value);
             serializeContents(value, g, provider);
             g.writeEndArray();
         }
-        
+
         @Override
         public void serializeContents(boolean[] value, JsonGenerator g, SerializerProvider provider)
-            throws IOException, JsonGenerationException
+            throws IOException
         {
             for (int i = 0, len = value.length; i < len; ++i) {
                 g.writeBoolean(value[i]);
@@ -182,18 +181,13 @@
 
         public ShortArraySerializer() { super(short[].class); }
         public ShortArraySerializer(ShortArraySerializer src, BeanProperty prop,
-                TypeSerializer vts, Boolean unwrapSingle) {
-            super(src, prop, vts, unwrapSingle);
+                 Boolean unwrapSingle) {
+            super(src, prop, unwrapSingle);
         }
 
         @Override
         public JsonSerializer<?> _withResolved(BeanProperty prop,Boolean unwrapSingle) {
-            return new ShortArraySerializer(this, prop, _valueTypeSerializer, unwrapSingle);
-        }
-
-        @Override
-        public ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer vts) {
-            return new ShortArraySerializer(this, _property, vts, _unwrapSingle);
+            return new ShortArraySerializer(this, prop, unwrapSingle);
         }
 
         @Override
@@ -209,7 +203,7 @@
         
         @Override
         public boolean isEmpty(SerializerProvider prov, short[] value) {
-            return (value == null) || (value.length == 0);
+            return value.length == 0;
         }
 
         @Override
@@ -220,16 +214,13 @@
         @Override
         public final void serialize(short[] value, JsonGenerator g, SerializerProvider provider) throws IOException
         {
-        	final int len = value.length;
-            if (len == 1) {
-                if (((_unwrapSingle == null) &&
-                        provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED))
-                        || (_unwrapSingle == Boolean.TRUE)) {
-                    serializeContents(value, g, provider);
-                    return;
-                }
+            final int len = value.length;
+            if ((len == 1) && _shouldUnwrapSingle(provider)) {
+                serializeContents(value, g, provider);
+                return;
             }
             g.writeStartArray(len);
+            g.setCurrentValue(value);
             serializeContents(value, g, provider);
             g.writeEndArray();
         }
@@ -237,16 +228,8 @@
         @SuppressWarnings("cast")
         @Override
         public void serializeContents(short[] value, JsonGenerator g, SerializerProvider provider)
-            throws IOException, JsonGenerationException
+            throws IOException
         {
-            if (_valueTypeSerializer != null) {
-                for (int i = 0, len = value.length; i < len; ++i) {
-                    _valueTypeSerializer.writeTypePrefixForScalar(null, g, Short.TYPE);
-                    g.writeNumber(value[i]);
-                    _valueTypeSerializer.writeTypeSuffixForScalar(null, g);
-                }
-                return;
-            }
             for (int i = 0, len = value.length; i < len; ++i) {
                 g.writeNumber((int)value[i]);
             }
@@ -273,7 +256,7 @@
      * they are most likely to be textual data, and should be written as
      * Strings, not arrays of entries.
      *<p>
-     * NOTE: since it is NOT serialized as an array, can not use AsArraySerializer as base
+     * NOTE: since it is NOT serialized as an array, cannot use AsArraySerializer as base
      */
     @JacksonStdImpl
     public static class CharArraySerializer extends StdSerializer<char[]>
@@ -282,16 +265,17 @@
         
         @Override
         public boolean isEmpty(SerializerProvider prov, char[] value) {
-            return (value == null) || (value.length == 0);
+            return value.length == 0;
         }
         
         @Override
         public void serialize(char[] value, JsonGenerator g, SerializerProvider provider)
-            throws IOException, JsonGenerationException
+            throws IOException
         {
             // [JACKSON-289] allows serializing as 'sparse' char array too:
             if (provider.isEnabled(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS)) {
                 g.writeStartArray(value.length);
+                g.setCurrentValue(value);
                 _writeArrayContents(g, value);
                 g.writeEndArray();
             } else {
@@ -302,22 +286,25 @@
         @Override
         public void serializeWithType(char[] value, JsonGenerator g, SerializerProvider provider,
                 TypeSerializer typeSer)
-            throws IOException, JsonGenerationException
+            throws IOException
         {
             // [JACKSON-289] allows serializing as 'sparse' char array too:
-            if (provider.isEnabled(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS)) {
-                typeSer.writeTypePrefixForArray(value, g);
+            final boolean asArray = provider.isEnabled(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS);
+            WritableTypeId typeIdDef;
+            if (asArray) {
+                typeIdDef = typeSer.writeTypePrefix(g,
+                        typeSer.typeId(value, JsonToken.START_ARRAY));
                 _writeArrayContents(g, value);
-                typeSer.writeTypeSuffixForArray(value, g);
             } else { // default is to write as simple String
-                typeSer.writeTypePrefixForScalar(value, g);
+                typeIdDef = typeSer.writeTypePrefix(g,
+                        typeSer.typeId(value, JsonToken.VALUE_STRING));
                 g.writeString(value, 0, value.length);
-                typeSer.writeTypeSuffixForScalar(value, g);
             }
+            typeSer.writeTypeSuffix(g, typeIdDef);
         }
 
         private final void _writeArrayContents(JsonGenerator g, char[] value)
-            throws IOException, JsonGenerationException
+            throws IOException
         {
             for (int i = 0, len = value.length; i < len; ++i) {
                 g.writeString(value, i, 1);
@@ -385,7 +372,7 @@
         
         @Override
         public boolean isEmpty(SerializerProvider prov, int[] value) {
-            return (value == null) || (value.length == 0);
+            return value.length == 0;
         }
 
         @Override
@@ -397,13 +384,9 @@
         public final void serialize(int[] value, JsonGenerator g, SerializerProvider provider) throws IOException
         {
             final int len = value.length;
-            if (len == 1) {
-                if (((_unwrapSingle == null) &&
-                        provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED))
-                        || (_unwrapSingle == Boolean.TRUE)) {
-                    serializeContents(value, g, provider);
-                    return;
-                }
+            if ((len == 1) && _shouldUnwrapSingle(provider)) {
+                serializeContents(value, g, provider);
+                return;
             }
             // 11-May-2016, tatu: As per [core#277] we have efficient `writeArray(...)` available
             g.setCurrentValue(value);
@@ -440,18 +423,13 @@
 
         public LongArraySerializer() { super(long[].class); }
         public LongArraySerializer(LongArraySerializer src, BeanProperty prop,
-                TypeSerializer vts, Boolean unwrapSingle) {
-            super(src, prop, vts, unwrapSingle);
+                Boolean unwrapSingle) {
+            super(src, prop, unwrapSingle);
         }
 
         @Override
         public JsonSerializer<?> _withResolved(BeanProperty prop,Boolean unwrapSingle) {
-            return new LongArraySerializer(this, prop, _valueTypeSerializer, unwrapSingle);
-        }
-
-        @Override
-        public ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer vts) {
-            return new LongArraySerializer(this, _property, vts, _unwrapSingle);
+            return new LongArraySerializer(this, prop, unwrapSingle);
         }
 
         @Override
@@ -467,7 +445,7 @@
         
         @Override
         public boolean isEmpty(SerializerProvider prov, long[] value) {
-            return (value == null) || (value.length == 0);
+            return value.length == 0;
         }
 
         @Override
@@ -479,13 +457,9 @@
         public final void serialize(long[] value, JsonGenerator g, SerializerProvider provider) throws IOException
         {
             final int len = value.length;
-            if (len == 1) {
-                if (((_unwrapSingle == null) &&
-                        provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED))
-                        || (_unwrapSingle == Boolean.TRUE)) {
-                    serializeContents(value, g, provider);
-                    return;
-                }
+            if ((len == 1) && _shouldUnwrapSingle(provider)) {
+                serializeContents(value, g, provider);
+                return;
             }
             // 11-May-2016, tatu: As per [core#277] we have efficient `writeArray(...)` available
             g.setCurrentValue(value);
@@ -496,15 +470,6 @@
         public void serializeContents(long[] value, JsonGenerator g, SerializerProvider provider)
             throws IOException
         {
-            if (_valueTypeSerializer != null) {
-                for (int i = 0, len = value.length; i < len; ++i) {
-                    _valueTypeSerializer.writeTypePrefixForScalar(null, g, Long.TYPE);
-                    g.writeNumber(value[i]);
-                    _valueTypeSerializer.writeTypeSuffixForScalar(null, g);
-                }
-                return;
-            }
-            
             for (int i = 0, len = value.length; i < len; ++i) {
                 g.writeNumber(value[i]);
             }
@@ -536,18 +501,13 @@
             super(float[].class);
         }
         public FloatArraySerializer(FloatArraySerializer src, BeanProperty prop,
-                TypeSerializer vts, Boolean unwrapSingle) {
-            super(src, prop, vts, unwrapSingle);
-        }
-
-        @Override
-        public ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer vts) {
-            return new FloatArraySerializer(this, _property, vts, _unwrapSingle);
+                Boolean unwrapSingle) {
+            super(src, prop, unwrapSingle);
         }
 
         @Override
         public JsonSerializer<?> _withResolved(BeanProperty prop,Boolean unwrapSingle) {
-            return new FloatArraySerializer(this, prop, _valueTypeSerializer, unwrapSingle);
+            return new FloatArraySerializer(this, prop, unwrapSingle);
         }
 
         @Override
@@ -563,7 +523,7 @@
         
         @Override
         public boolean isEmpty(SerializerProvider prov, float[] value) {
-            return (value == null) || (value.length == 0);
+            return value.length == 0;
         }
 
         @Override
@@ -575,31 +535,20 @@
         public final void serialize(float[] value, JsonGenerator g, SerializerProvider provider) throws IOException
         {
             final int len = value.length;
-            if (len == 1) {
-                if (((_unwrapSingle == null) &&
-                        provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED))
-                        || (_unwrapSingle == Boolean.TRUE)) {
-                    serializeContents(value, g, provider);
-                    return;
-                }
+            if ((len == 1) && _shouldUnwrapSingle(provider)) {
+                serializeContents(value, g, provider);
+                return;
             }
             g.writeStartArray(len);
+            g.setCurrentValue(value);
             serializeContents(value, g, provider);
             g.writeEndArray();
         }
         
         @Override
         public void serializeContents(float[] value, JsonGenerator g, SerializerProvider provider)
-            throws IOException, JsonGenerationException
+            throws IOException
         {
-            if (_valueTypeSerializer != null) {
-                for (int i = 0, len = value.length; i < len; ++i) {
-                    _valueTypeSerializer.writeTypePrefixForScalar(null, g, Float.TYPE);
-                    g.writeNumber(value[i]);
-                    _valueTypeSerializer.writeTypeSuffixForScalar(null, g);
-                }
-                return;
-            }
             for (int i = 0, len = value.length; i < len; ++i) {
                 g.writeNumber(value[i]);
             }
@@ -661,7 +610,7 @@
         
         @Override
         public boolean isEmpty(SerializerProvider prov, double[] value) {
-            return (value == null) || (value.length == 0);
+            return value.length == 0;
         }
 
         @Override
@@ -673,16 +622,12 @@
         public final void serialize(double[] value, JsonGenerator g, SerializerProvider provider) throws IOException
         {
             final int len = value.length;
-            if (len == 1) {
-                if (((_unwrapSingle == null) &&
-                        provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED))
-                        || (_unwrapSingle == Boolean.TRUE)) {
-                    serializeContents(value, g, provider);
-                    return;
-                }
+            if ((len == 1) && _shouldUnwrapSingle(provider)) {
+                serializeContents(value, g, provider);
+                return;
             }
-            // 11-May-2016, tatu: As per [core#277] we have efficient `writeArray(...)` available
             g.setCurrentValue(value);
+            // 11-May-2016, tatu: As per [core#277] we have efficient `writeArray(...)` available
             g.writeArray(value, 0, value.length);
         }
 
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdDelegatingSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdDelegatingSerializer.java
index 067ebc6..f162101 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdDelegatingSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdDelegatingSerializer.java
@@ -9,6 +9,7 @@
 import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
 import com.fasterxml.jackson.databind.ser.ContextualSerializer;
 import com.fasterxml.jackson.databind.ser.ResolvableSerializer;
+import com.fasterxml.jackson.databind.util.ClassUtil;
 import com.fasterxml.jackson.databind.util.Converter;
 
 import java.io.IOException;
@@ -83,9 +84,7 @@
     protected StdDelegatingSerializer withDelegate(Converter<Object,?> converter,
             JavaType delegateType, JsonSerializer<?> delegateSerializer)
     {
-        if (getClass() != StdDelegatingSerializer.class) {
-            throw new IllegalStateException("Sub-class "+getClass().getName()+" must override 'withDelegate'");
-        }
+        ClassUtil.verifyMustOverride(StdDelegatingSerializer.class, this, "withDelegate");
         return new StdDelegatingSerializer(converter, delegateType, delegateSerializer);
     }
     
@@ -116,9 +115,8 @@
             if (delegateType == null) {
                 delegateType = _converter.getOutputType(provider.getTypeFactory());
             }
-            /* 02-Apr-2015, tatu: For "dynamic case", where type is only specified as
-             *    java.lang.Object (or missing generic), [databind#731]
-             */
+            // 02-Apr-2015, tatu: For "dynamic case", where type is only specified as
+            //    java.lang.Object (or missing generic), [databind#731]
             if (!delegateType.isJavaLangObject()) {
                 delSer = provider.findValueSerializer(delegateType);
             }
@@ -186,15 +184,12 @@
     }
 
     @Override
-    @Deprecated // since 2.5
-    public boolean isEmpty(Object value) {
-        return isEmpty(null, value);
-    }
-
-    @Override
     public boolean isEmpty(SerializerProvider prov, Object value)
     {
         Object delegateValue = convertValue(value);
+        if (delegateValue == null) {
+            return true;
+        }
         if (_delegateSerializer == null) { // best we can do for now, too costly to look up
             return (value == null);
         }
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdJdkSerializers.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdJdkSerializers.java
index a87ce64..b99bab3 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdJdkSerializers.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdJdkSerializers.java
@@ -27,15 +27,13 @@
         HashMap<Class<?>,Object> sers = new HashMap<Class<?>,Object>();
 
         // First things that 'toString()' can handle
-        final ToStringSerializer sls = ToStringSerializer.instance;
+        sers.put(java.net.URL.class, new ToStringSerializer(java.net.URL.class));
+        sers.put(java.net.URI.class, new ToStringSerializer(java.net.URI.class));
 
-        sers.put(java.net.URL.class, sls);
-        sers.put(java.net.URI.class, sls);
-
-        sers.put(Currency.class, sls);
+        sers.put(Currency.class, new ToStringSerializer(Currency.class));
         sers.put(UUID.class, new UUIDSerializer());
-        sers.put(java.util.regex.Pattern.class, sls);
-        sers.put(Locale.class, sls);
+        sers.put(java.util.regex.Pattern.class, new ToStringSerializer(java.util.regex.Pattern.class));
+        sers.put(Locale.class, new ToStringSerializer(Locale.class));
 
         // then atomic types (note: AtomicReference defined elsewhere)
         sers.put(AtomicBoolean.class, AtomicBooleanSerializer.class);
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdKeySerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdKeySerializer.java
index fceba52..c62e444 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdKeySerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdKeySerializer.java
@@ -1,17 +1,13 @@
 package com.fasterxml.jackson.databind.ser.std;
 
 import java.io.IOException;
-import java.lang.reflect.Type;
-import java.util.Date;
 
 import com.fasterxml.jackson.core.*;
 import com.fasterxml.jackson.databind.*;
-import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
 
 /**
- * Specialized serializer that can be used as the generic key
- * serializer, when serializing {@link java.util.Map}s to JSON
- * Objects.
+ * Specialized serializer that can be used as the generic key serializer,
+ * when serializing {@link java.util.Map}s to JSON Objects.
  *
  * @deprecated Since 2.8, use {@link StdKeySerializers.Default} instead.
  */
@@ -23,42 +19,7 @@
 
     @Override
     public void serialize(Object value, JsonGenerator g, SerializerProvider provider) throws IOException {
-        String str;
-        Class<?> cls = value.getClass();
-
-        if (cls == String.class) {
-            str = (String) value;
-        } else if (cls.isEnum()) {
-            // 24-Sep-2015, tatu: Minor improvement over older (2.6.2 and before) code: at least
-            //     use name/toString() variation for as per configuration
-            if (provider.isEnabled(SerializationFeature.WRITE_ENUMS_USING_TO_STRING)) {
-                str = value.toString();
-            } else {
-                Enum<?> en = (Enum<?>) value;
-                if (provider.isEnabled(SerializationFeature.WRITE_ENUMS_USING_TO_STRING)) {
-                    str = String.valueOf(en.ordinal());
-                } else {
-                    str = en.name();
-                }
-            }
-        } else if (value instanceof Date) {
-            provider.defaultSerializeDateKey((Date) value, g);
-            return;
-        } else if (cls == Class.class) {
-            str = ((Class<?>) value).getName();
-        } else {
-            str = value.toString();
-        }
-        g.writeFieldName(str);
-    }
-
-    @Override
-    public JsonNode getSchema(SerializerProvider provider, Type typeHint) throws JsonMappingException {
-        return createSchemaNode("string");
-    }
-
-    @Override
-    public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException {
-        visitStringFormat(visitor, typeHint);
+        // 19-Oct-2016, tatu: Simplified to bare essentials since this is deprecated
+        g.writeFieldName(value.toString());
     }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdKeySerializers.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdKeySerializers.java
index 369e659..70fe67f 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdKeySerializers.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdKeySerializers.java
@@ -8,18 +8,17 @@
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
 import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap;
+import com.fasterxml.jackson.databind.util.ClassUtil;
 import com.fasterxml.jackson.databind.util.EnumValues;
 
 @SuppressWarnings("serial")
-public class StdKeySerializers
+public abstract class StdKeySerializers
 {
     @SuppressWarnings("deprecation")
     protected final static JsonSerializer<Object> DEFAULT_KEY_SERIALIZER = new StdKeySerializer();
 
     protected final static JsonSerializer<Object> DEFAULT_STRING_SERIALIZER = new StringKeySerializer();
 
-    private StdKeySerializers() { }
-
     /**
      * @param config Serialization configuration in use, may be needed in choosing
      *    serializer to use
@@ -31,7 +30,7 @@
             Class<?> rawKeyType, boolean useDefault)
     {
         // 24-Sep-2015, tatu: Important -- should ONLY consider types for which `@JsonValue`
-        //    can not be used, since caller has not yet checked for that annotation
+        //    cannot be used, since caller has not yet checked for that annotation
         //    This is why Enum types are not handled here quite yet
 
         // [databind#943: Use a dynamic key serializer if we are not given actual
@@ -42,6 +41,15 @@
         if (rawKeyType == String.class) {
             return DEFAULT_STRING_SERIALIZER;
         }
+        if (rawKeyType.isPrimitive()) {
+            rawKeyType = ClassUtil.wrapperType(rawKeyType);
+        }
+        if (rawKeyType == Integer.class) {
+            return new Default(Default.TYPE_INTEGER, rawKeyType);
+        }
+        if (rawKeyType == Long.class) {
+            return new Default(Default.TYPE_LONG, rawKeyType);
+        }
         if (rawKeyType.isPrimitive() || Number.class.isAssignableFrom(rawKeyType)) {
             // 28-Jun-2016, tatu: Used to just return DEFAULT_KEY_SERIALIZER, but makes
             //   more sense to use simpler one directly
@@ -60,8 +68,12 @@
         if (rawKeyType == java.util.UUID.class) {
             return new Default(Default.TYPE_TO_STRING, rawKeyType);
         }
+        if (rawKeyType == byte[].class) {
+            return new Default(Default.TYPE_BYTE_ARRAY, rawKeyType);
+        }
         if (useDefault) {
-            return DEFAULT_KEY_SERIALIZER;
+            // 19-Oct-2016, tatu: Used to just return DEFAULT_KEY_SERIALIZER but why not:
+            return new Default(Default.TYPE_TO_STRING, rawKeyType);
         }
         return null;
     }
@@ -91,7 +103,8 @@
                         EnumValues.constructFromName(config, (Class<Enum<?>>) rawKeyType));
             }
         }
-        return DEFAULT_KEY_SERIALIZER;
+        // 19-Oct-2016, tatu: Used to just return DEFAULT_KEY_SERIALIZER but why not:
+        return new Default(Default.TYPE_TO_STRING, rawKeyType);
     }
 
     /**
@@ -121,7 +134,10 @@
         final static int TYPE_CALENDAR = 2;
         final static int TYPE_CLASS = 3;
         final static int TYPE_ENUM = 4;
-        final static int TYPE_TO_STRING = 5;
+        final static int TYPE_INTEGER = 5; // since 2.9
+        final static int TYPE_LONG = 6; // since 2.9
+        final static int TYPE_BYTE_ARRAY = 7; // since 2.9
+        final static int TYPE_TO_STRING = 8;
 
         protected final int _typeId;
         
@@ -159,6 +175,16 @@
                     g.writeFieldName(key);
                 }
                 break;
+            case TYPE_INTEGER:
+            case TYPE_LONG:
+                g.writeFieldId(((Number) value).longValue());
+                break;
+            case TYPE_BYTE_ARRAY:
+                {
+                    String encoded = provider.getConfig().getBase64Variant().encode((byte[]) value);
+                    g.writeFieldName(encoded);
+                }
+                break;
             case TYPE_TO_STRING:
             default:
                 g.writeFieldName(value.toString());
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdScalarSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdScalarSerializer.java
index e64a3d6..916a9c9 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdScalarSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdScalarSerializer.java
@@ -4,6 +4,7 @@
 import java.lang.reflect.Type;
 
 import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.type.WritableTypeId;
 import com.fasterxml.jackson.databind.JavaType;
 import com.fasterxml.jackson.databind.JsonMappingException;
 import com.fasterxml.jackson.databind.JsonNode;
@@ -39,9 +40,11 @@
     public void serializeWithType(T value, JsonGenerator g, SerializerProvider provider,
             TypeSerializer typeSer) throws IOException
     {
-        typeSer.writeTypePrefixForScalar(value, g);
+        // NOTE: need not really be string; just indicates "scalar of some kind"
+        WritableTypeId typeIdDef = typeSer.writeTypePrefix(g,
+                typeSer.typeId(value, JsonToken.VALUE_STRING));
         serialize(value, g, provider);
-        typeSer.writeTypeSuffixForScalar(value, g);
+        typeSer.writeTypeSuffix(g, typeIdDef);
     }
 
     @Override
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdSerializer.java
index a1108e8..52ed126 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdSerializer.java
@@ -3,6 +3,9 @@
 import java.io.IOException;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Type;
+import java.util.Collection;
+import java.util.IdentityHashMap;
+import java.util.Map;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.annotation.JsonInclude;
@@ -30,21 +33,17 @@
     extends JsonSerializer<T>
     implements JsonFormatVisitable, SchemaAware, java.io.Serializable
 {
-    /**
-     * Unique key we use to store a temporary lock, to prevent infinite recursion
-     * when resolving content converters (see [databind#357]).
-     *<p>
-     * NOTE: may need to revisit this if nested content converters are needed; if so,
-     * may need to create per-call lock object. But let's start with a simpler
-     * solution for now.
-     *
-     * @since 2.7
-     */
-    private final static Object CONVERTING_CONTENT_CONVERTER_LOCK = new Object();
-
     private static final long serialVersionUID = 1L;
 
     /**
+     * Key used for storing a lock object to prevent infinite recursion when
+     * constructing converting serializers.
+     *
+     * @since 2.9
+     */
+    private final static Object KEY_CONTENT_CONVERTER_LOCK = new Object();
+    
+    /**
      * Nominal type supported, usually declared type of
      * property for which serializer is used.
      */
@@ -138,7 +137,7 @@
     {
         ObjectNode schema = (ObjectNode) getSchema(provider, typeHint);
         if (!isOptional) {
-    		    schema.put("required", !isOptional);
+            schema.put("required", !isOptional);
         }
         return schema;
     }
@@ -149,13 +148,9 @@
     /**********************************************************
      */
 
-    protected ObjectNode createObjectNode() {
-        return JsonNodeFactory.instance.objectNode();
-    }
-    
     protected ObjectNode createSchemaNode(String type)
     {
-        ObjectNode schema = createObjectNode();
+        ObjectNode schema = JsonNodeFactory.instance.objectNode();
         schema.put("type", type);
         return schema;
     }
@@ -177,9 +172,7 @@
      */
     protected void visitStringFormat(JsonFormatVisitorWrapper visitor, JavaType typeHint)
             throws JsonMappingException {
-        if (visitor != null) {
-            /*JsonStringFormatVisitor v2 =*/ visitor.expectStringFormat(typeHint);
-        }
+        /*JsonStringFormatVisitor v2 =*/ visitor.expectStringFormat(typeHint);
     }
 
     /**
@@ -193,11 +186,9 @@
             JsonValueFormat format)
         throws JsonMappingException
     {
-        if (visitor != null) {
-            JsonStringFormatVisitor v2 = visitor.expectStringFormat(typeHint);
-            if (v2 != null) {
-                v2.format(format);
-            }
+        JsonStringFormatVisitor v2 = visitor.expectStringFormat(typeHint);
+        if (v2 != null) {
+            v2.format(format);
         }
     }
 
@@ -211,13 +202,9 @@
             NumberType numberType)
         throws JsonMappingException
     {
-        if (visitor != null) {
-            JsonIntegerFormatVisitor v2 = visitor.expectIntegerFormat(typeHint);
-            if (v2 != null) {
-                if (numberType != null) {
-                    v2.numberType(numberType);
-                }
-            }
+        JsonIntegerFormatVisitor v2 = visitor.expectIntegerFormat(typeHint);
+        if (_neitherNull(v2, numberType)) {
+            v2.numberType(numberType);
         }
     }
 
@@ -232,15 +219,13 @@
             NumberType numberType, JsonValueFormat format)
         throws JsonMappingException
     {
-        if (visitor != null) {
-            JsonIntegerFormatVisitor v2 = visitor.expectIntegerFormat(typeHint);
-            if (v2 != null) {
-                if (numberType != null) {
-                    v2.numberType(numberType);
-                }
-                if (format != null) {
-                    v2.format(format);
-                }
+        JsonIntegerFormatVisitor v2 = visitor.expectIntegerFormat(typeHint);
+        if (v2 != null) {
+            if (numberType != null) {
+                v2.numberType(numberType);
+            }
+            if (format != null) {
+                v2.format(format);
             }
         }
     }
@@ -255,11 +240,9 @@
             NumberType numberType)
         throws JsonMappingException
     {
-        if (visitor != null) {
-            JsonNumberFormatVisitor v2 = visitor.expectNumberFormat(typeHint);
-            if (v2 != null) {
-                v2.numberType(numberType);
-            }
+        JsonNumberFormatVisitor v2 = visitor.expectNumberFormat(typeHint);
+        if (v2 != null) {
+            v2.numberType(numberType);
         }
     }
 
@@ -270,13 +253,9 @@
             JsonSerializer<?> itemSerializer, JavaType itemType)
         throws JsonMappingException
     {
-        if (visitor != null) {
-            JsonArrayFormatVisitor v2 = visitor.expectArrayFormat(typeHint);
-            if (v2 != null) {
-                if (itemSerializer != null) {
-                    v2.itemsFormat(itemSerializer, itemType);
-                }
-            }
+        JsonArrayFormatVisitor v2 = visitor.expectArrayFormat(typeHint);
+        if (_neitherNull(v2, itemSerializer)) {
+            v2.itemsFormat(itemSerializer, itemType);
         }
     }
 
@@ -287,14 +266,12 @@
             JsonFormatTypes itemType)
         throws JsonMappingException
     {
-        if (visitor != null) {
-            JsonArrayFormatVisitor v2 = visitor.expectArrayFormat(typeHint);
-            if (v2 != null) {
-                v2.itemsFormat(itemType);
-            }
+        JsonArrayFormatVisitor v2 = visitor.expectArrayFormat(typeHint);
+        if (v2 != null) {
+            v2.itemsFormat(itemType);
         }
     }
-    
+
     /*
     /**********************************************************
     /* Helper methods for exception handling
@@ -324,20 +301,16 @@
         while (t instanceof InvocationTargetException && t.getCause() != null) {
             t = t.getCause();
         }
-        // Errors and "plain" IOExceptions to be passed as is
-        if (t instanceof Error) {
-            throw (Error) t;
-        }
+        // Errors and "plain" to be passed as is
+        ClassUtil.throwIfError(t);
         // Ditto for IOExceptions... except for mapping exceptions!
         boolean wrap = (provider == null) || provider.isEnabled(SerializationFeature.WRAP_EXCEPTIONS);
         if (t instanceof IOException) {
             if (!wrap || !(t instanceof JsonMappingException)) {
                 throw (IOException) t;
             }
-        } else if (!wrap) { // [JACKSON-407] -- allow disabling wrapping for unchecked exceptions
-            if (t instanceof RuntimeException) {
-                throw (RuntimeException) t;
-            }
+        } else if (!wrap) {
+            ClassUtil.throwIfRTE(t);
         }
         // Need to add reference information
         throw JsonMappingException.wrapWithPath(t, bean, fieldName);
@@ -351,19 +324,15 @@
             t = t.getCause();
         }
         // Errors are to be passed as is
-        if (t instanceof Error) {
-            throw (Error) t;
-        }
+        ClassUtil.throwIfError(t);
         // Ditto for IOExceptions... except for mapping exceptions!
         boolean wrap = (provider == null) || provider.isEnabled(SerializationFeature.WRAP_EXCEPTIONS);
         if (t instanceof IOException) {
             if (!wrap || !(t instanceof JsonMappingException)) {
                 throw (IOException) t;
             }
-        } else if (!wrap) { // [JACKSON-407] -- allow disabling wrapping for unchecked exceptions
-            if (t instanceof RuntimeException) {
-                throw (RuntimeException) t;
-            }
+        } else if (!wrap) {
+            ClassUtil.throwIfRTE(t);
         }
         // Need to add reference information
         throw JsonMappingException.wrapWithPath(t, bean, index);
@@ -383,34 +352,50 @@
      * @param existingSerializer (optional) configured content
      *    serializer if one already exists.
      * 
-     * @since 2.2
+     * @since 2.9
      */
+    protected JsonSerializer<?> findContextualConvertingSerializer(SerializerProvider provider,
+            BeanProperty property, JsonSerializer<?> existingSerializer)
+        throws JsonMappingException
+    {
+        // 08-Dec-2016, tatu: to fix [databind#357], need to prevent recursive calls for
+        //     same property
+        @SuppressWarnings("unchecked")
+        Map<Object,Object> conversions = (Map<Object,Object>) provider.getAttribute(KEY_CONTENT_CONVERTER_LOCK);
+        if (conversions != null) {
+            Object lock = conversions.get(property);
+            if (lock != null) {
+                return existingSerializer;
+            }
+        } else {
+            conversions = new IdentityHashMap<>();
+            provider.setAttribute(KEY_CONTENT_CONVERTER_LOCK, conversions);
+        }
+        conversions.put(property, Boolean.TRUE);
+        try {
+            JsonSerializer<?> ser = findConvertingContentSerializer(provider, property, existingSerializer);
+            if (ser != null) {
+                return provider.handleSecondaryContextualization(ser, property);
+            }
+        } finally {
+            conversions.remove(property);
+        }
+        return existingSerializer;
+    }
+
+    /**
+     * @deprecated Since 2.9 use {link {@link #findContextualConvertingSerializer} instead
+     */
+    @Deprecated
     protected JsonSerializer<?> findConvertingContentSerializer(SerializerProvider provider,
             BeanProperty prop, JsonSerializer<?> existingSerializer)
         throws JsonMappingException
     {
-        /* 19-Oct-2014, tatu: As per [databind#357], need to avoid infinite loop
-         *   when applying contextual content converter; this is not ideal way,
-         *   but should work for most cases.
-         */
-        Object ob = provider.getAttribute(CONVERTING_CONTENT_CONVERTER_LOCK);
-        if (ob != null) {
-            if (ob == Boolean.TRUE) { // just to ensure it's value we added.
-                return existingSerializer;
-            }
-        }
-
         final AnnotationIntrospector intr = provider.getAnnotationIntrospector();
-        if (intr != null && prop != null) {
+        if (_neitherNull(intr, prop)) {
             AnnotatedMember m = prop.getMember();
             if (m != null) {
-                provider.setAttribute(CONVERTING_CONTENT_CONVERTER_LOCK, Boolean.TRUE);
-                Object convDef;
-                try {
-                    convDef = intr.findSerializationContentConverter(m);
-                } finally {
-                    provider.setAttribute(CONVERTING_CONTENT_CONVERTER_LOCK, null);
-                }
+                Object convDef = intr.findSerializationContentConverter(m);
                 if (convDef != null) {
                     Converter<Object,Object> conv = provider.converterInstance(prop.getMember(), convDef);
                     JavaType delegateType = conv.getOutputType(provider.getTypeFactory());
@@ -438,8 +423,8 @@
         FilterProvider filters = provider.getFilterProvider();
         // Not ok to miss the provider, if a filter is declared to be needed.
         if (filters == null) {
-            throw JsonMappingException.from(provider,
-                    "Can not resolve PropertyFilter with id '"+filterId+"'; no FilterProvider configured");
+            provider.reportBadDefinition(handledType(),
+                    "Cannot resolve PropertyFilter with id '"+filterId+"'; no FilterProvider configured");
         }
         // But whether unknown ids are ok just depends on filter provider; if we get null that's fine
         return filters.findPropertyFilter(filterId, valueToFilter);
@@ -534,4 +519,18 @@
     protected boolean isDefaultSerializer(JsonSerializer<?> serializer) {
         return ClassUtil.isJacksonStdImpl(serializer);
     }
+
+    /**
+     * @since 2.9
+     */
+    protected final static boolean _neitherNull(Object a, Object b) {
+        return (a != null) && (b != null);
+    }
+
+    /**
+     * @since 2.9
+     */
+    protected final static boolean _nonEmpty(Collection<?> c) {
+        return (c != null) && !c.isEmpty();
+    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/StringSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/StringSerializer.java
index 3ec907c..d3c621c 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/StringSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StringSerializer.java
@@ -11,6 +11,7 @@
 import com.fasterxml.jackson.databind.SerializerProvider;
 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
 
 /**
  * This is the special serializer for regular {@link java.lang.String}s.
@@ -22,26 +23,17 @@
 public final class StringSerializer
 // NOTE: generic parameter changed from String to Object in 2.6, to avoid
 //   use of bridge methods
-    extends NonTypedScalarSerializerBase<Object>
+// In 2.9, removed use of intermediate type `NonTypedScalarSerializerBase`
+    extends StdScalarSerializer<Object>
 {
     private static final long serialVersionUID = 1L;
 
     public StringSerializer() { super(String.class, false); }
 
-    /**
-     * For Strings, both null and Empty String qualify for emptiness.
-     */
-    @Override
-    @Deprecated
-    public boolean isEmpty(Object value) {
-        String str = (String) value;
-        return (str == null) || (str.length() == 0);
-    }
-
     @Override
     public boolean isEmpty(SerializerProvider prov, Object value) {
         String str = (String) value;
-        return (str == null) || (str.length() == 0);
+        return str.length() == 0;
     }
 
     @Override
@@ -50,6 +42,14 @@
     }
 
     @Override
+    public final void serializeWithType(Object value, JsonGenerator gen, SerializerProvider provider,
+            TypeSerializer typeSer) throws IOException
+    {
+        // no type info, just regular serialization
+        gen.writeString((String) value);
+    }
+
+    @Override
     public JsonNode getSchema(SerializerProvider provider, Type typeHint) {
         return createSchemaNode("string", true);
     }
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/TimeZoneSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/TimeZoneSerializer.java
index 3ad88de..6bfb214 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/TimeZoneSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/TimeZoneSerializer.java
@@ -4,6 +4,7 @@
 import java.util.TimeZone;
 
 import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.type.WritableTypeId;
 import com.fasterxml.jackson.databind.SerializerProvider;
 import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
 
@@ -13,15 +14,18 @@
     public TimeZoneSerializer() { super(TimeZone.class); }
 
     @Override
-    public void serialize(TimeZone value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
-        jgen.writeString(value.getID());
+    public void serialize(TimeZone value, JsonGenerator g, SerializerProvider provider) throws IOException {
+        g.writeString(value.getID());
     }
 
     @Override
-    public void serializeWithType(TimeZone value, JsonGenerator jgen, SerializerProvider provider, TypeSerializer typeSer) throws IOException {
+    public void serializeWithType(TimeZone value, JsonGenerator g,
+            SerializerProvider provider, TypeSerializer typeSer) throws IOException
+    {
         // Better ensure we don't use specific sub-classes:
-        typeSer.writeTypePrefixForScalar(value, jgen, TimeZone.class);
-        serialize(value, jgen, provider);
-        typeSer.writeTypeSuffixForScalar(value, jgen);
+        WritableTypeId typeIdDef = typeSer.writeTypePrefix(g,
+                typeSer.typeId(value, TimeZone.class, JsonToken.VALUE_STRING));
+        serialize(value, g, provider);
+        typeSer.writeTypeSuffix(g, typeIdDef);
     }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/ToStringSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/ToStringSerializer.java
index 5d3aa91..eb73422 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/ToStringSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/ToStringSerializer.java
@@ -4,6 +4,7 @@
 import java.lang.reflect.Type;
 
 import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.type.WritableTypeId;
 import com.fasterxml.jackson.databind.JavaType;
 import com.fasterxml.jackson.databind.JsonMappingException;
 import com.fasterxml.jackson.databind.JsonNode;
@@ -48,13 +49,9 @@
 
     @Override
     public boolean isEmpty(SerializerProvider prov, Object value) {
-        if (value == null) {
-            return true;
-        }
-        String str = value.toString();
-        return str.isEmpty();
+        return value.toString().isEmpty();
     }
-    
+
     @Override
     public void serialize(Object value, JsonGenerator gen, SerializerProvider provider)
         throws IOException
@@ -74,20 +71,21 @@
      * change this behavior.
      */
     @Override
-    public void serializeWithType(Object value, JsonGenerator gen, SerializerProvider provider,
+    public void serializeWithType(Object value, JsonGenerator g, SerializerProvider provider,
             TypeSerializer typeSer)
         throws IOException
     {
-        typeSer.writeTypePrefixForScalar(value, gen);
-        serialize(value, gen, provider);
-        typeSer.writeTypeSuffixForScalar(value, gen);
+        WritableTypeId typeIdDef = typeSer.writeTypePrefix(g,
+                typeSer.typeId(value, JsonToken.VALUE_STRING));
+        serialize(value, g, provider);
+        typeSer.writeTypeSuffix(g, typeIdDef);
     }
-    
+
     @Override
     public JsonNode getSchema(SerializerProvider provider, Type typeHint) throws JsonMappingException {
         return createSchemaNode("string", true);
     }
-    
+
     @Override
     public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException
     {
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/TokenBufferSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/TokenBufferSerializer.java
index d7539b2..a7624b0 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/TokenBufferSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/TokenBufferSerializer.java
@@ -4,6 +4,7 @@
 import java.lang.reflect.Type;
 
 import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.type.WritableTypeId;
 import com.fasterxml.jackson.databind.JavaType;
 import com.fasterxml.jackson.databind.JsonMappingException;
 import com.fasterxml.jackson.databind.JsonNode;
@@ -15,7 +16,7 @@
 
 /**
  * We also want to directly support serialization of {@link TokenBuffer};
- * and since it is part of core package, it can not implement
+ * and since it is part of core package, it cannot implement
  * {@link com.fasterxml.jackson.databind.JsonSerializable}
  * (which is only included in the mapper package)
  */
@@ -28,7 +29,7 @@
 
     @Override
     public void serialize(TokenBuffer value, JsonGenerator jgen, SerializerProvider provider)
-            throws IOException
+        throws IOException
     {
         value.serialize(jgen);
     }
@@ -44,14 +45,17 @@
      * than doing introspection on both serialization and deserialization.
      */
     @Override
-    public final void serializeWithType(TokenBuffer value, JsonGenerator jgen, SerializerProvider provider,
-            TypeSerializer typeSer) throws IOException
+    public final void serializeWithType(TokenBuffer value, JsonGenerator g,
+            SerializerProvider provider, TypeSerializer typeSer) throws IOException
     {
-        typeSer.writeTypePrefixForScalar(value, jgen);
-        serialize(value, jgen, provider);
-        typeSer.writeTypeSuffixForScalar(value, jgen);
+        // 28-Jun-2017, tatu: As per javadoc, not sure what to report as likely shape. Could
+        //    even look into first actual token inside... but, for now let's keep it simple
+        WritableTypeId typeIdDef = typeSer.writeTypePrefix(g,
+                typeSer.typeId(value, JsonToken.VALUE_EMBEDDED_OBJECT));
+        serialize(value, g, provider);
+        typeSer.writeTypeSuffix(g, typeIdDef);
     }
-    
+
     @Override
     public JsonNode getSchema(SerializerProvider provider, Type typeHint)
     {
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/UUIDSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/UUIDSerializer.java
index cd9e5a1..21768e7 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/UUIDSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/UUIDSerializer.java
@@ -25,9 +25,6 @@
     @Override
     public boolean isEmpty(SerializerProvider prov, UUID value)
     {
-        if (value == null) {
-            return true;
-        }
         // Null UUID is empty, so...
         if (value.getLeastSignificantBits() == 0L
                 && value.getMostSignificantBits() == 0L) {
diff --git a/src/main/java/com/fasterxml/jackson/databind/type/ArrayType.java b/src/main/java/com/fasterxml/jackson/databind/type/ArrayType.java
index 24ac759..ef3e6aa 100644
--- a/src/main/java/com/fasterxml/jackson/databind/type/ArrayType.java
+++ b/src/main/java/com/fasterxml/jackson/databind/type/ArrayType.java
@@ -125,7 +125,7 @@
     }
 
     private JavaType _reportUnsupported() {
-        throw new UnsupportedOperationException("Can not narrow or widen array types");
+        throw new UnsupportedOperationException("Cannot narrow or widen array types");
     }
 
     /*
diff --git a/src/main/java/com/fasterxml/jackson/databind/type/CollectionLikeType.java b/src/main/java/com/fasterxml/jackson/databind/type/CollectionLikeType.java
index f355160..2bd9bbf 100644
--- a/src/main/java/com/fasterxml/jackson/databind/type/CollectionLikeType.java
+++ b/src/main/java/com/fasterxml/jackson/databind/type/CollectionLikeType.java
@@ -86,7 +86,7 @@
         if (baseType instanceof TypeBase) {
             return new CollectionLikeType((TypeBase) baseType, elementType);
         }
-        throw new IllegalArgumentException("Can not upgrade from an instance of "+baseType.getClass());
+        throw new IllegalArgumentException("Cannot upgrade from an instance of "+baseType.getClass());
     }
 
     @Override
diff --git a/src/main/java/com/fasterxml/jackson/databind/type/MapLikeType.java b/src/main/java/com/fasterxml/jackson/databind/type/MapLikeType.java
index f1d1ee0..d62356b 100644
--- a/src/main/java/com/fasterxml/jackson/databind/type/MapLikeType.java
+++ b/src/main/java/com/fasterxml/jackson/databind/type/MapLikeType.java
@@ -66,7 +66,7 @@
             return new MapLikeType((TypeBase) baseType, keyT, valueT);
         }
         throw new IllegalArgumentException(
-                "Can not upgrade from an instance of " + baseType.getClass());
+                "Cannot upgrade from an instance of " + baseType.getClass());
     }
 
     @Deprecated
diff --git a/src/main/java/com/fasterxml/jackson/databind/type/PlaceholderForType.java b/src/main/java/com/fasterxml/jackson/databind/type/PlaceholderForType.java
index 54ed402..7193d98 100644
--- a/src/main/java/com/fasterxml/jackson/databind/type/PlaceholderForType.java
+++ b/src/main/java/com/fasterxml/jackson/databind/type/PlaceholderForType.java
@@ -83,6 +83,7 @@
         return _unsupported();
     }
 
+    @SuppressWarnings("deprecation")
     @Override
     protected JavaType _narrow(Class<?> subclass) {
         return _unsupported();
diff --git a/src/main/java/com/fasterxml/jackson/databind/type/ReferenceType.java b/src/main/java/com/fasterxml/jackson/databind/type/ReferenceType.java
index e8c6962..f9ac7a3 100644
--- a/src/main/java/com/fasterxml/jackson/databind/type/ReferenceType.java
+++ b/src/main/java/com/fasterxml/jackson/databind/type/ReferenceType.java
@@ -70,7 +70,7 @@
         if (baseType instanceof TypeBase) {
             return new ReferenceType((TypeBase) baseType, refdType);
         }
-        throw new IllegalArgumentException("Can not upgrade from an instance of "+baseType.getClass());
+        throw new IllegalArgumentException("Cannot upgrade from an instance of "+baseType.getClass());
     }
 
     /**
diff --git a/src/main/java/com/fasterxml/jackson/databind/type/ResolvedRecursiveType.java b/src/main/java/com/fasterxml/jackson/databind/type/ResolvedRecursiveType.java
index 53e061c..d45e1d6 100644
--- a/src/main/java/com/fasterxml/jackson/databind/type/ResolvedRecursiveType.java
+++ b/src/main/java/com/fasterxml/jackson/databind/type/ResolvedRecursiveType.java
@@ -112,7 +112,7 @@
         if (o == this) return true;
         if (o == null) return false;
         if (o.getClass() == getClass()) {
-            // 16-Jun-2017, tatu: as per [databind#1658], can not do recursive call since
+            // 16-Jun-2017, tatu: as per [databind#1658], cannot do recursive call since
             //    there is likely to be a cycle...
 
             // but... true or false?
diff --git a/src/main/java/com/fasterxml/jackson/databind/type/SimpleType.java b/src/main/java/com/fasterxml/jackson/databind/type/SimpleType.java
index 1fb2433..9e97f75 100644
--- a/src/main/java/com/fasterxml/jackson/databind/type/SimpleType.java
+++ b/src/main/java/com/fasterxml/jackson/databind/type/SimpleType.java
@@ -104,14 +104,14 @@
          * Map/Collection entries are constructed
          */
         if (Map.class.isAssignableFrom(cls)) {
-            throw new IllegalArgumentException("Can not construct SimpleType for a Map (class: "+cls.getName()+")");
+            throw new IllegalArgumentException("Cannot construct SimpleType for a Map (class: "+cls.getName()+")");
         }
         if (Collection.class.isAssignableFrom(cls)) {
-            throw new IllegalArgumentException("Can not construct SimpleType for a Collection (class: "+cls.getName()+")");
+            throw new IllegalArgumentException("Cannot construct SimpleType for a Collection (class: "+cls.getName()+")");
         }
         // ... and while we are at it, not array types either
         if (cls.isArray()) {
-            throw new IllegalArgumentException("Can not construct SimpleType for an array (class: "+cls.getName()+")");
+            throw new IllegalArgumentException("Cannot construct SimpleType for an array (class: "+cls.getName()+")");
         }
         TypeBindings b = TypeBindings.emptyBindings();
         return new SimpleType(cls, b,
@@ -127,7 +127,7 @@
         }
         // Should we check that there is a sub-class relationship?
         // 15-Jan-2016, tatu: Almost yes, but there are some complications with
-        //    placeholder values (`Void`, `NoClass`), so can not quite do yet.
+        //    placeholder values (`Void`, `NoClass`), so cannot quite do yet.
         // TODO: fix in 2.9
         if (!_class.isAssignableFrom(subclass)) {
             /*
@@ -162,13 +162,13 @@
             }
         }
         // should not get here but...
-        throw new IllegalArgumentException("Internal error: Can not resolve sub-type for Class "+subclass.getName()+" to "
+        throw new IllegalArgumentException("Internal error: Cannot resolve sub-type for Class "+subclass.getName()+" to "
                 +_class.getName());
     }
     
     @Override
     public JavaType withContentType(JavaType contentType) {
-        throw new IllegalArgumentException("Simple types have no content types; can not call withContentType()");
+        throw new IllegalArgumentException("Simple types have no content types; cannot call withContentType()");
     }
     
     @Override
@@ -182,7 +182,7 @@
     @Override
     public JavaType withContentTypeHandler(Object h) {
         // no content type, so:
-        throw new IllegalArgumentException("Simple types have no content types; can not call withContenTypeHandler()");
+        throw new IllegalArgumentException("Simple types have no content types; cannot call withContenTypeHandler()");
     }
 
     @Override
@@ -196,7 +196,7 @@
     @Override
     public  SimpleType withContentValueHandler(Object h) {
         // no content type, so:
-        throw new IllegalArgumentException("Simple types have no content types; can not call withContenValueHandler()");
+        throw new IllegalArgumentException("Simple types have no content types; cannot call withContenValueHandler()");
     }
 
     @Override
diff --git a/src/main/java/com/fasterxml/jackson/databind/type/TypeBase.java b/src/main/java/com/fasterxml/jackson/databind/type/TypeBase.java
index 056c727..a98d6ed 100644
--- a/src/main/java/com/fasterxml/jackson/databind/type/TypeBase.java
+++ b/src/main/java/com/fasterxml/jackson/databind/type/TypeBase.java
@@ -5,6 +5,8 @@
 
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.core.type.WritableTypeId;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
 
@@ -162,13 +164,14 @@
      */
 
     @Override
-    public void serializeWithType(JsonGenerator gen, SerializerProvider provider,
+    public void serializeWithType(JsonGenerator g, SerializerProvider provider,
             TypeSerializer typeSer)
-        throws IOException, JsonProcessingException
+        throws IOException
     {
-        typeSer.writeTypePrefixForScalar(this, gen);
-        this.serialize(gen, provider);
-        typeSer.writeTypeSuffixForScalar(this, gen);
+        WritableTypeId typeIdDef = new WritableTypeId(this, JsonToken.VALUE_STRING);
+        typeSer.writeTypePrefix(g, typeIdDef);
+        this.serialize(g, provider);
+        typeSer.writeTypeSuffix(g, typeIdDef);
     }
 
     @Override
diff --git a/src/main/java/com/fasterxml/jackson/databind/type/TypeBindings.java b/src/main/java/com/fasterxml/jackson/databind/type/TypeBindings.java
index 6752f20..872608a 100644
--- a/src/main/java/com/fasterxml/jackson/databind/type/TypeBindings.java
+++ b/src/main/java/com/fasterxml/jackson/databind/type/TypeBindings.java
@@ -4,6 +4,7 @@
 import java.util.*;
 
 import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.util.ClassUtil;
 
 /**
  * Helper class used for resolving type parameters for given class
@@ -109,7 +110,7 @@
         }
         // Check here to give better error message
         if (names.length != types.length) {
-            throw new IllegalArgumentException("Can not create TypeBindings for class "+erasedType.getName()
+            throw new IllegalArgumentException("Cannot create TypeBindings for class "+erasedType.getName()
                    +" with "+types.length+" type parameter"
                    +((types.length == 1) ? "" : "s")+": class expects "+names.length);
         }
@@ -122,7 +123,7 @@
         TypeVariable<?>[] vars = TypeParamStash.paramsFor1(erasedType);
         int varLen = (vars == null) ? 0 : vars.length;
         if (varLen != 1) {
-            throw new IllegalArgumentException("Can not create TypeBindings for class "+erasedType.getName()
+            throw new IllegalArgumentException("Cannot create TypeBindings for class "+erasedType.getName()
                     +" with 1 type parameter: class expects "+varLen);
         }
         return new TypeBindings(new String[] { vars[0].getName() },
@@ -135,7 +136,7 @@
         TypeVariable<?>[] vars = TypeParamStash.paramsFor2(erasedType);
         int varLen = (vars == null) ? 0 : vars.length;
         if (varLen != 2) {
-            throw new IllegalArgumentException("Can not create TypeBindings for class "+erasedType.getName()
+            throw new IllegalArgumentException("Cannot create TypeBindings for class "+erasedType.getName()
                     +" with 2 type parameters: class expects "+varLen);
         }
         return new TypeBindings(new String[] { vars[0].getName(), vars[1].getName() },
@@ -155,7 +156,7 @@
             return EMPTY;
         }
         if (varLen != 1) {
-            throw new IllegalArgumentException("Can not create TypeBindings for class "+erasedType.getName()
+            throw new IllegalArgumentException("Cannot create TypeBindings for class "+erasedType.getName()
                     +" with 1 type parameter: class expects "+varLen);
         }
         return new TypeBindings(new String[] { vars[0].getName() },
@@ -183,7 +184,7 @@
         }
         // Check here to give better error message
         if (names.length != types.length) {
-            throw new IllegalArgumentException("Can not create TypeBindings for class "+erasedType.getName()
+            throw new IllegalArgumentException("Cannot create TypeBindings for class "+erasedType.getName()
                    +" with "+types.length+" type parameter"
                    +((types.length == 1) ? "" : "s")+": class expects "+names.length);
         }
@@ -335,7 +336,9 @@
     @Override public boolean equals(Object o)
     {
         if (o == this) return true;
-        if (o == null || o.getClass() != getClass()) return false;
+        if (!ClassUtil.hasClass(o, getClass())) {
+            return false;
+        }
         TypeBindings other = (TypeBindings) o;
         int len = _types.length;
         if (len != other.size()) {
diff --git a/src/main/java/com/fasterxml/jackson/databind/type/TypeFactory.java b/src/main/java/com/fasterxml/jackson/databind/type/TypeFactory.java
index 46910eb..8b0ac18 100644
--- a/src/main/java/com/fasterxml/jackson/databind/type/TypeFactory.java
+++ b/src/main/java/com/fasterxml/jackson/databind/type/TypeFactory.java
@@ -297,15 +297,13 @@
                 prob = ClassUtil.getRootCause(e);
             }
         }
-        if (prob instanceof RuntimeException) {
-            throw (RuntimeException) prob;
-        }
+        ClassUtil.throwIfRTE(prob);
         throw new ClassNotFoundException(prob.getMessage(), prob);
     }
     
     protected Class<?> classForName(String name, boolean initialize,
-                                   ClassLoader loader) throws ClassNotFoundException {
-    	return Class.forName(name, true, loader);
+            ClassLoader loader) throws ClassNotFoundException {
+        return Class.forName(name, true, loader);
     }
     
     protected Class<?> classForName(String name) throws ClassNotFoundException {
@@ -363,7 +361,7 @@
 
             // (1) Original target type has no generics -- just resolve subtype
             if (baseType.getBindings().isEmpty()) {
-                newType = _fromClass(null, subclass, EMPTY_BINDINGS);     
+                newType = _fromClass(null, subclass, EMPTY_BINDINGS);
                 break;
             }
             // (2) A small set of "well-known" List/Map subtypes where can take a short-cut
@@ -396,7 +394,7 @@
             // (3) Sub-class does not take type parameters -- just resolve subtype
             int typeParamCount = subclass.getTypeParameters().length;
             if (typeParamCount == 0) {
-                newType = _fromClass(null, subclass, TypeBindings.emptyBindings());     
+                newType = _fromClass(null, subclass, EMPTY_BINDINGS);
                 break;
             }
             // (4) If all else fails, do the full traversal using placeholders
@@ -457,6 +455,21 @@
             JavaType exp = expectedTypes.get(i);
             JavaType act = actualTypes.get(i);
             if (!_verifyAndResolvePlaceholders(exp, act)) {
+                // 14-May-2018, tatu: As per [databind#2034] it seems we better relax assignment
+                //   rules further -- at least likely "raw" (untyped, non-generic) base should probably
+                //   allow specialization.
+                if (exp.hasRawClass(Object.class)) {
+                    continue;
+                }
+                // 19-Apr-2018, tatu: Hack for [databind#1964] -- allow type demotion
+                //    for `java.util.Map` key type if (and only if) target type is
+                //    `java.lang.Object`
+                if (i == 0) {
+                    if (sourceType.hasRawClass(Map.class)
+                            && act.hasRawClass(Object.class)) {
+                        continue;
+                    }
+                }
                 return String.format("Type parameter #%d/%d differs; can not specialize %s with %s",
                         (i+1), len, exp.toCanonical(), act.toCanonical());
             }
@@ -654,7 +667,7 @@
     public JavaType constructType(Type type, JavaType contextType) {
         TypeBindings bindings;
         if (contextType == null) {
-            bindings = TypeBindings.emptyBindings();
+            bindings = EMPTY_BINDINGS;
         } else {
             bindings = contextType.getBindings();
             // 16-Nov-2016, tatu: Unfortunately as per [databind#1456] this can't
@@ -721,11 +734,22 @@
      * for contained types.
      */
     public CollectionType constructCollectionType(Class<? extends Collection> collectionClass,
-            JavaType elementType) {
-        // 19-Oct-2015, tatu: Allow case of no-type-variables, since it seems likely to be
-        //    a valid use case here
-        return (CollectionType) _fromClass(null, collectionClass,
-                TypeBindings.create(collectionClass, elementType));
+            JavaType elementType)
+    {
+        TypeBindings bindings = TypeBindings.createIfNeeded(collectionClass, elementType);
+        CollectionType result = (CollectionType) _fromClass(null, collectionClass, bindings);
+        // 17-May-2017, tatu: As per [databind#1415], we better verify bound values if (but only if)
+        //    type being resolved was non-generic (i.e.element type was ignored)
+        if (bindings.isEmpty() && (elementType != null)) {
+            JavaType t = result.findSuperType(Collection.class);
+            JavaType realET = t.getContentType();
+            if (!realET.equals(elementType)) {
+                throw new IllegalArgumentException(String.format(
+                        "Non-generic Collection class %s did not resolve to something with element type %s but %s ",
+                        ClassUtil.nameOf(collectionClass), elementType, realET));
+            }
+        }
+        return result;
     }
 
     /**
@@ -779,8 +803,26 @@
      * for contained types.
      */
     public MapType constructMapType(Class<? extends Map> mapClass, JavaType keyType, JavaType valueType) {
-        return (MapType) _fromClass(null, mapClass,
-                TypeBindings.create(mapClass, keyType, valueType));
+        TypeBindings bindings = TypeBindings.createIfNeeded(mapClass, new JavaType[] { keyType, valueType });
+        MapType result = (MapType) _fromClass(null, mapClass, bindings);
+        // 17-May-2017, tatu: As per [databind#1415], we better verify bound values if (but only if)
+        //    type being resolved was non-generic (i.e.element type was ignored)
+        if (bindings.isEmpty()) {
+            JavaType t = result.findSuperType(Map.class);
+            JavaType realKT = t.getKeyType();
+            if (!realKT.equals(keyType)) {
+                throw new IllegalArgumentException(String.format(
+                        "Non-generic Map class %s did not resolve to something with key type %s but %s ",
+                        ClassUtil.nameOf(mapClass), keyType, realKT));
+            }
+            JavaType realVT = t.getContentType();
+            if (!realVT.equals(valueType)) {
+                throw new IllegalArgumentException(String.format(
+                        "Non-generic Map class %s did not resolve to something with value type %s but %s ",
+                        ClassUtil.nameOf(mapClass), valueType, realVT));
+            }
+        }
+        return result;
     }
 
     /**
@@ -868,15 +910,15 @@
      * type <code>List&lt;Set&lt;Integer>></code>, you could
      * call
      *<pre>
-     *  JavaType inner = TypeFactory.constructParametrizedType(Set.class, Set.class, Integer.class);
-     *  return TypeFactory.constructParametrizedType(ArrayList.class, List.class, inner);
+     *  JavaType inner = TypeFactory.constructParametricType(Set.class, Set.class, Integer.class);
+     *  return TypeFactory.constructParametricType(ArrayList.class, List.class, inner);
      *</pre>
      *<p>
      * The reason for first two arguments to be separate is that parameterization may
      * apply to a super-type. For example, if generic type was instead to be
      * constructed for <code>ArrayList&lt;Integer></code>, the usual call would be:
      *<pre>
-     *  TypeFactory.constructParametrizedType(ArrayList.class, List.class, Integer.class);
+     *  TypeFactory.constructParametricType(ArrayList.class, List.class, Integer.class);
      *</pre>
      * since parameterization is applied to {@link java.util.List}.
      * In most cases distinction does not matter, but there are types where it does;
@@ -904,15 +946,15 @@
      * type <code>List&lt;Set&lt;Integer>></code>, you could
      * call
      *<pre>
-     *  JavaType inner = TypeFactory.constructParametrizedType(Set.class, Set.class, Integer.class);
-     *  return TypeFactory.constructParametrizedType(ArrayList.class, List.class, inner);
+     *  JavaType inner = TypeFactory.constructParametricType(Set.class, Set.class, Integer.class);
+     *  return TypeFactory.constructParametricType(ArrayList.class, List.class, inner);
      *</pre>
      *<p>
      * The reason for first two arguments to be separate is that parameterization may
      * apply to a super-type. For example, if generic type was instead to be
      * constructed for <code>ArrayList&lt;Integer></code>, the usual call would be:
      *<pre>
-     *  TypeFactory.constructParametrizedType(ArrayList.class, List.class, Integer.class);
+     *  TypeFactory.constructParametricType(ArrayList.class, List.class, Integer.class);
      *</pre>
      * since parameterization is applied to {@link java.util.List}.
      * In most cases distinction does not matter, but there are types where it does;
@@ -932,7 +974,10 @@
 
     /**
      * @since 2.5 -- but will probably deprecated in 2.7 or 2.8 (not needed with 2.7)
+     *
+     * @deprecated since 2.9 Use {@link #constructParametricType(Class,JavaType...)} instead
      */
+    @Deprecated
     public JavaType constructParametrizedType(Class<?> parametrized, Class<?> parametersFor,
             JavaType... parameterTypes)
     {
@@ -941,7 +986,10 @@
 
     /**
      * @since 2.5 -- but will probably deprecated in 2.7 or 2.8 (not needed with 2.7)
+     *
+     * @deprecated since 2.9 Use {@link #constructParametricType(Class,Class...)} instead
      */
+    @Deprecated
     public JavaType constructParametrizedType(Class<?> parametrized, Class<?> parametersFor,
             Class<?>... parameterClasses)
     {
@@ -1041,7 +1089,7 @@
                 vt = typeParams.get(1);
                 break;
             default:
-                throw new IllegalArgumentException("Strange Map type "+rawClass.getName()+": can not determine type parameters");
+                throw new IllegalArgumentException("Strange Map type "+rawClass.getName()+": cannot determine type parameters");
             }
         }
         return MapType.construct(rawClass, bindings, superClass, superInterfaces, kt, vt);
@@ -1058,7 +1106,7 @@
         } else if (typeParams.size() == 1) {
             ct = typeParams.get(0);
         } else {
-            throw new IllegalArgumentException("Strange Collection type "+rawClass.getName()+": can not determine type parameters");
+            throw new IllegalArgumentException("Strange Collection type "+rawClass.getName()+": cannot determine type parameters");
         }
         return CollectionType.construct(rawClass, bindings, superClass, superInterfaces, ct);
     }
@@ -1074,7 +1122,7 @@
         } else if (typeParams.size() == 1) {
             ct = typeParams.get(0);
         } else {
-            throw new IllegalArgumentException("Strange Reference type "+rawClass.getName()+": can not determine type parameters");
+            throw new IllegalArgumentException("Strange Reference type "+rawClass.getName()+": cannot determine type parameters");
         }
         return ReferenceType.construct(rawClass, bindings, superClass, superInterfaces, ct);
     }
@@ -1253,7 +1301,7 @@
                 superClass = null;
                 superInterfaces = _resolveSuperInterfaces(context, rawType, bindings);
             } else {
-                // Note: even Enums can implement interfaces, so can not drop those
+                // Note: even Enums can implement interfaces, so cannot drop those
                 superClass = _resolveSuperClass(context, rawType, bindings);
                 superInterfaces = _resolveSuperInterfaces(context, rawType, bindings);
             }
@@ -1322,7 +1370,7 @@
             JavaType superClass, JavaType[] superInterfaces)
     {
         if (bindings == null) {
-            bindings = TypeBindings.emptyBindings();
+            bindings = EMPTY_BINDINGS;
         }
         
         // Quite simple when we resolving exact class/interface; start with that
@@ -1410,6 +1458,7 @@
     {
         // ideally should find it via bindings:
         final String name = var.getName();
+if (bindings == null) throw new Error("No Bindings!");
         JavaType type = bindings.findBoundType(name);
         if (type != null) {
             return type;
diff --git a/src/main/java/com/fasterxml/jackson/databind/type/TypeModifier.java b/src/main/java/com/fasterxml/jackson/databind/type/TypeModifier.java
index 0b59011..7210041 100644
--- a/src/main/java/com/fasterxml/jackson/databind/type/TypeModifier.java
+++ b/src/main/java/com/fasterxml/jackson/databind/type/TypeModifier.java
@@ -11,7 +11,7 @@
  * replace) basic type instance factory constructs.
  * This is typically needed to support creation of
  * {@link MapLikeType} and {@link CollectionLikeType} instances,
- * as those can not be constructed in generic fashion.
+ * as those cannot be constructed in generic fashion.
  */
 public abstract class TypeModifier
 {
@@ -29,7 +29,7 @@
      *   construct instance of primary type itself
      * 
      * @return Actual type instance to use; usually either <code>type</code> (as is or with
-     *    modifications), or a newly constructed type instance based on it. Can not be null.
+     *    modifications), or a newly constructed type instance based on it. Cannot be null.
      */
     public abstract JavaType modifyType(JavaType type, Type jdkType, TypeBindings context,
             TypeFactory typeFactory);
diff --git a/src/main/java/com/fasterxml/jackson/databind/type/TypeParser.java b/src/main/java/com/fasterxml/jackson/databind/type/TypeParser.java
index 1817e0a..3cfcb72 100644
--- a/src/main/java/com/fasterxml/jackson/databind/type/TypeParser.java
+++ b/src/main/java/com/fasterxml/jackson/databind/type/TypeParser.java
@@ -3,6 +3,7 @@
 import java.util.*;
 
 import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.util.ClassUtil;
 
 /**
  * Simple recursive-descent parser for parsing canonical {@link JavaType}
@@ -80,10 +81,8 @@
         try {
             return _factory.findClass(className);
         } catch (Exception e) {
-            if (e instanceof RuntimeException) {
-                throw (RuntimeException) e;
-            }
-            throw _problem(tokens, "Can not locate class '"+className+"', problem: "+e.getMessage());
+            ClassUtil.throwIfRTE(e);
+            throw _problem(tokens, "Cannot locate class '"+className+"', problem: "+e.getMessage());
         }
     }
 
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/AccessPattern.java b/src/main/java/com/fasterxml/jackson/databind/util/AccessPattern.java
new file mode 100644
index 0000000..03cabcd
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/util/AccessPattern.java
@@ -0,0 +1,31 @@
+package com.fasterxml.jackson.databind.util;
+
+/**
+ * Enumeration used to indicate required access pattern for providers:
+ * this can sometimes be used to optimize out dynamic calls.
+ * The main difference is between constant values (which can be resolved once)
+ * and dynamic ones (which must be resolved anew every time).
+ */
+public enum AccessPattern {
+    /**
+     * Value that indicates that provider never returns anything other than
+     * Java `null`.
+     */
+    ALWAYS_NULL,
+
+    /**
+     * Value that indicates that provider will always return a constant
+     * value, regardless of when it is called; and also that it never
+     * uses `context` argument (which may then be passed as `null`)
+     */
+    CONSTANT,
+
+    /**
+     * Value that indicates that provider may return different values
+     * at different times (often a freshly constructed empty container),
+     * and thus must be called every time "null replacement" value is
+     * needed.
+     */
+    DYNAMIC
+    ;
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/Annotations.java b/src/main/java/com/fasterxml/jackson/databind/util/Annotations.java
index cdb7ea9..d362403 100644
--- a/src/main/java/com/fasterxml/jackson/databind/util/Annotations.java
+++ b/src/main/java/com/fasterxml/jackson/databind/util/Annotations.java
@@ -18,6 +18,16 @@
     public <A extends Annotation> A get(Class<A> cls);
 
     /**
+     * @since 2.9
+     */
+    public boolean has(Class<?> cls);
+
+    /**
+     * @since 2.9
+     */
+    public boolean hasOneOf(Class<? extends Annotation>[] annoClasses);
+
+    /**
      * Returns number of annotation entries in this collection.
      */
     public int size();
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/ArrayBuilders.java b/src/main/java/com/fasterxml/jackson/databind/util/ArrayBuilders.java
index 834a96e..ae2939a 100644
--- a/src/main/java/com/fasterxml/jackson/databind/util/ArrayBuilders.java
+++ b/src/main/java/com/fasterxml/jackson/databind/util/ArrayBuilders.java
@@ -162,7 +162,7 @@
             @Override
             public boolean equals(Object other) {
                 if (other == this) return true;
-                if (other == null || other.getClass() != defaultValueType) {
+                if (!ClassUtil.hasClass(other, defaultValueType)) {
                     return false;
                 }
                 if (Array.getLength(other) != length) return false;
@@ -184,76 +184,15 @@
 
     public static <T> HashSet<T> arrayToSet(T[] elements)
     {
-        HashSet<T> result = new HashSet<T>();
         if (elements != null) {
-            for (T elem : elements) {
-                result.add(elem);
+            int len = elements.length;
+            HashSet<T> result = new HashSet<T>(len);
+            for (int i = 0; i < len; ++i) {
+                result.add(elements[i]);
             }
+            return result;
         }
-        return result;
-    }
-
-    public static <T> ArrayList<T> arrayToList(T[] elements)
-    {
-        ArrayList<T> result = new ArrayList<T>();
-        if (elements != null) {
-            for (T elem : elements) {
-                result.add(elem);
-            }
-        }
-        return result;
-    }
-
-    public static <T> HashSet<T> setAndArray(Set<T> set, T[] elements)
-    {
-        HashSet<T> result = new HashSet<T>();
-        if (set != null) {
-            result.addAll(set);
-        }
-        if (elements != null) {
-            for (T value : elements) {
-                result.add(value);
-            }
-        }
-        return result;
-    }
-    
-    /**
-     * Helper method for adding specified element to a List, but also
-     * considering case where the List may not have been yet constructed
-     * (that is, null is passed instead).
-     * 
-     * @param list List to add to; may be null to indicate that a new
-     *    List is to be constructed
-     * @param element Element to add to list
-     * 
-     * @return List in which element was added; either <code>list</code>
-     *   (if it was not null), or a newly constructed List.
-     */
-    public static <T> List<T> addToList(List<T> list, T element)
-    {
-        if (list == null) {
-            list = new ArrayList<T>();
-        }
-        list.add(element);
-        return list;
-    }
-
-    /**
-     * Helper method for constructing a new array that contains specified
-     * element followed by contents of the given array. No checking is done
-     * to see if element being inserted is duplicate.
-     */
-    public static <T> T[] insertInList(T[] array, T element)
-    {
-        int len = array.length;
-        @SuppressWarnings("unchecked")
-        T[] result = (T[]) Array.newInstance(array.getClass().getComponentType(), len+1);
-        if (len > 0) {
-            System.arraycopy(array, 0, result, 1, len);
-        }
-        result[0] = element;
-        return result;
+        return new HashSet<T>();
     }
 
     /**
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/BeanUtil.java b/src/main/java/com/fasterxml/jackson/databind/util/BeanUtil.java
index 5fcaa8d..54462ce 100644
--- a/src/main/java/com/fasterxml/jackson/databind/util/BeanUtil.java
+++ b/src/main/java/com/fasterxml/jackson/databind/util/BeanUtil.java
@@ -1,5 +1,11 @@
 package com.fasterxml.jackson.databind.util;
 
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.JavaType;
 import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
 
 /**
@@ -77,6 +83,7 @@
     /**
      * @since 2.5
      */
+    @Deprecated // since 2.9, not used any more
     public static String okNameForSetter(AnnotatedMethod am, boolean stdNaming) {
         String name = okNameForMutator(am, "set", stdNaming);
         if ((name != null) 
@@ -103,33 +110,52 @@
 
     /*
     /**********************************************************
-    /* Handling property names, deprecated methods
+    /* Value defaulting helpers
     /**********************************************************
      */
+    
+    /**
+     * Accessor used to find out "default value" to use for comparing values to
+     * serialize, to determine whether to exclude value from serialization with
+     * inclusion type of {@link com.fasterxml.jackson.annotation.JsonInclude.Include#NON_DEFAULT}.
+     *<p>
+     * Default logic is such that for primitives and wrapper types for primitives, expected
+     * defaults (0 for `int` and `java.lang.Integer`) are returned; for Strings, empty String,
+     * and for structured (Maps, Collections, arrays) and reference types, criteria
+     * {@link com.fasterxml.jackson.annotation.JsonInclude.Include#NON_DEFAULT}
+     * is used.
+     *
+     * @since 2.7
+     */
+    public static Object getDefaultValue(JavaType type)
+    {
+        // 06-Nov-2015, tatu: Returning null is fine for Object types; but need special
+        //   handling for primitives since they are never passed as nulls.
+        Class<?> cls = type.getRawClass();
 
-    @Deprecated // since 2.5
-    public static String okNameForGetter(AnnotatedMethod am) {
-        return okNameForGetter(am, false);
-    }
-
-    @Deprecated // since 2.5
-    public static String okNameForRegularGetter(AnnotatedMethod am, String name) {
-        return okNameForRegularGetter(am, name, false);
-    }
-
-    @Deprecated // since 2.5
-    public static String okNameForIsGetter(AnnotatedMethod am, String name) {
-        return okNameForIsGetter(am, name, false);
-    }
-
-    @Deprecated // since 2.5
-    public static String okNameForSetter(AnnotatedMethod am) {
-        return okNameForSetter(am, false);
-    }
-
-    @Deprecated // since 2.5
-    public static String okNameForMutator(AnnotatedMethod am, String prefix) {
-        return okNameForMutator(am, prefix, false);
+        // 30-Sep-2016, tatu: Also works for Wrappers, so both `Integer.TYPE` and `Integer.class`
+        //    would return `Integer.TYPE`
+        Class<?> prim = ClassUtil.primitiveType(cls);
+        if (prim != null) {
+            return ClassUtil.defaultValue(prim);
+        }
+        if (type.isContainerType() || type.isReferenceType()) {
+            return JsonInclude.Include.NON_EMPTY;
+        }
+        if (cls == String.class) {
+            return "";
+        }
+        // 09-Mar-2016, tatu: Not sure how far this path we want to go but for now
+        //   let's add `java.util.Date` and `java.util.Calendar`, as per [databind#1550]
+        if (type.isTypeOrSubTypeOf(Date.class)) {
+            return new Date(0L);
+        }
+        if (type.isTypeOrSubTypeOf(Calendar.class)) {
+            Calendar c = new GregorianCalendar();
+            c.setTimeInMillis(0L);
+            return c;
+        }
+        return null;
     }
 
     /*
@@ -139,8 +165,8 @@
      */
 
     /**
-     * This method was added to address [JACKSON-53]: need to weed out
-     * CGLib-injected "getCallbacks". 
+     * This method was added to address the need to weed out
+     * CGLib-injected "getCallbacks" method. 
      * At this point caller has detected a potential getter method
      * with name "getCallbacks" and we need to determine if it is
      * indeed injectect by Cglib. We do this by verifying that the
@@ -150,25 +176,21 @@
     {
         Class<?> rt = am.getRawType();
         // Ok, first: must return an array type
-        if (rt == null || !rt.isArray()) {
-            return false;
-        }
-        /* And that type needs to be "net.sf.cglib.proxy.Callback".
-         * Theoretically could just be a type that implements it, but
-         * for now let's keep things simple, fix if need be.
-         */
-        Class<?> compType = rt.getComponentType();
-        // Actually, let's just verify it's a "net.sf.cglib.*" class/interface
-        String pkgName = ClassUtil.getPackageName(compType);
-        if (pkgName != null) {
-            if (pkgName.contains(".cglib")) {
-                if (pkgName.startsWith("net.sf.cglib")
-                    // also, as per [JACKSON-177]
-                    || pkgName.startsWith("org.hibernate.repackage.cglib")
-                    // and [core#674]
-                    || pkgName.startsWith("org.springframework.cglib")
-                        ) {
-                    return true;
+        if (rt.isArray()) {
+            /* And that type needs to be "net.sf.cglib.proxy.Callback".
+             * Theoretically could just be a type that implements it, but
+             * for now let's keep things simple, fix if need be.
+             */
+            Class<?> compType = rt.getComponentType();
+            // Actually, let's just verify it's a "net.sf.cglib.*" class/interface
+            String pkgName = ClassUtil.getPackageName(compType);
+            if (pkgName != null) {
+                if (pkgName.contains(".cglib")) {
+                    return pkgName.startsWith("net.sf.cglib")
+                        // also, as per [JACKSON-177]
+                        || pkgName.startsWith("org.hibernate.repackage.cglib")
+                        // and [core#674]
+                        || pkgName.startsWith("org.springframework.cglib");
                 }
             }
         }
@@ -177,32 +199,22 @@
 
     /**
      * Similar to {@link #isCglibGetCallbacks}, need to suppress
-     * a cyclic reference to resolve [JACKSON-103]
+     * a cyclic reference.
      */
     protected static boolean isGroovyMetaClassSetter(AnnotatedMethod am)
     {
         Class<?> argType = am.getRawParameterType(0);
         String pkgName = ClassUtil.getPackageName(argType);
-        if (pkgName != null && pkgName.startsWith("groovy.lang")) {
-            return true;
-        }
-        return false;
+        return (pkgName != null) && pkgName.startsWith("groovy.lang");
     }
 
     /**
-     * Another helper method to deal with rest of [JACKSON-103]
+     * Another helper method to deal with Groovy's problematic metadata accessors
      */
     protected static boolean isGroovyMetaClassGetter(AnnotatedMethod am)
     {
-        Class<?> rt = am.getRawType();
-        if (rt == null || rt.isArray()) {
-            return false;
-        }
-        String pkgName = ClassUtil.getPackageName(rt);
-        if (pkgName != null && pkgName.startsWith("groovy.lang")) {
-            return true;
-        }
-        return false;
+        String pkgName = ClassUtil.getPackageName(am.getRawType());
+        return (pkgName != null) && pkgName.startsWith("groovy.lang");
     }
 
     /*
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/ClassUtil.java b/src/main/java/com/fasterxml/jackson/databind/util/ClassUtil.java
index 5d14255..1181f58 100644
--- a/src/main/java/com/fasterxml/jackson/databind/util/ClassUtil.java
+++ b/src/main/java/com/fasterxml/jackson/databind/util/ClassUtil.java
@@ -7,7 +7,9 @@
 import java.util.*;
 
 import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.DeserializationContext;
 import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonMappingException;
 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
 
 public final class ClassUtil
@@ -16,24 +18,8 @@
 
     private final static Annotation[] NO_ANNOTATIONS = new Annotation[0];
     private final static Ctor[] NO_CTORS = new Ctor[0];
-    
-    /*
-    /**********************************************************
-    /* Helper classes
-    /**********************************************************
-     */
 
-    /* 21-Feb-2016, tatu: Unfortunately `Collections.emptyIterator()` only
-     *   comes with JDK7, so we'll still have to include our bogus implementation
-     *   for as long as we want JDK6 runtime compatibility
-     */
-    private final static class EmptyIterator<T> implements Iterator<T> {
-        @Override public boolean hasNext() { return false; }
-        @Override public T next() { throw new NoSuchElementException(); }
-        @Override public void remove() { throw new UnsupportedOperationException(); }
-    }
-    
-    private final static EmptyIterator<?> EMPTY_ITERATOR = new EmptyIterator<Object>();
+    private final static Iterator<?> EMPTY_ITERATOR = Collections.emptyIterator();
 
     /*
     /**********************************************************
@@ -46,8 +32,6 @@
      */
     @SuppressWarnings("unchecked")
     public static <T> Iterator<T> emptyIterator() {
-// 21-Feb-2016, tatu: As per above, use a locally defined empty iterator
-//        return Collections.emptyIterator();
         return (Iterator<T>) EMPTY_ITERATOR;
     }
 
@@ -288,73 +272,43 @@
         return false;
     }
 
-    /*
-    /**********************************************************
-    /* Type name handling methods
-    /**********************************************************
-     */
-    
-    /**
-     * Helper method used to construct appropriate description
-     * when passed either type (Class) or an instance; in latter
-     * case, class of instance is to be used.
-     */
-    public static String getClassDescription(Object classOrInstance)
-    {
-        if (classOrInstance == null) {
-            return "unknown";
-        }
-        Class<?> cls = (classOrInstance instanceof Class<?>) ?
-            (Class<?>) classOrInstance : classOrInstance.getClass();
-        return cls.getName();
+    public static boolean isBogusClass(Class<?> cls) {
+        return (cls == Void.class || cls == Void.TYPE
+                || cls == com.fasterxml.jackson.databind.annotation.NoClass.class);
     }
 
-    /*
-    /**********************************************************
-    /* Class loading
-    /**********************************************************
-     */
+    public static boolean isNonStaticInnerClass(Class<?> cls) {
+        return !Modifier.isStatic(cls.getModifiers())
+                && (getEnclosingClass(cls) != null);
+    }
 
     /**
-     * @deprecated Since 2.6, use method in {@link com.fasterxml.jackson.databind.type.TypeFactory}.
+     * @since 2.7
      */
-    @Deprecated
-    public static Class<?> findClass(String className) throws ClassNotFoundException
+    public static boolean isObjectOrPrimitive(Class<?> cls) {
+        return (cls == CLS_OBJECT) || cls.isPrimitive();
+    }
+
+    /**
+     * @since 2.9
+     */
+    public static boolean hasClass(Object inst, Class<?> raw) {
+        // 10-Nov-2016, tatu: Could use `Class.isInstance()` if we didn't care
+        //    about being exactly that type
+        return (inst != null) && (inst.getClass() == raw);
+    }
+
+    /**
+     * @since 2.9
+     */
+    public static void verifyMustOverride(Class<?> expType, Object instance,
+            String method)
     {
-        // [JACKSON-597]: support primitive types (and void)
-        if (className.indexOf('.') < 0) {
-            if ("int".equals(className)) return Integer.TYPE;
-            if ("long".equals(className)) return Long.TYPE;
-            if ("float".equals(className)) return Float.TYPE;
-            if ("double".equals(className)) return Double.TYPE;
-            if ("boolean".equals(className)) return Boolean.TYPE;
-            if ("byte".equals(className)) return Byte.TYPE;
-            if ("char".equals(className)) return Character.TYPE;
-            if ("short".equals(className)) return Short.TYPE;
-            if ("void".equals(className)) return Void.TYPE;
+        if (instance.getClass() != expType) {
+            throw new IllegalStateException(String.format(
+                    "Sub-class %s (of class %s) must override method '%s'",
+                instance.getClass().getName(), expType.getName(), method));
         }
-        // Two-phase lookup: first using context ClassLoader; then default
-        Throwable prob = null;
-        ClassLoader loader = Thread.currentThread().getContextClassLoader();
-        
-        if (loader != null) {
-            try {
-                return Class.forName(className, true, loader);
-            } catch (Exception e) {
-                prob = getRootCause(e);
-            }
-        }
-        try {
-            return Class.forName(className);
-        } catch (Exception e) {
-            if (prob == null) {
-                prob = getRootCause(e);
-            }
-        }
-        if (prob instanceof RuntimeException) {
-            throw (RuntimeException) prob;
-        }
-        throw new ClassNotFoundException(prob.getMessage(), prob);
     }
 
     /*
@@ -388,11 +342,56 @@
 
     /*
     /**********************************************************
-    /* Exception handling
+    /* Exception handling; simple re-throw
     /**********************************************************
      */
 
     /**
+     * Helper method that will check if argument is an {@link Error},
+     * and if so, (re)throw it; otherwise just return
+     *
+     * @since 2.9
+     */
+    public static Throwable throwIfError(Throwable t) {
+        if (t instanceof Error) {
+            throw (Error) t;
+        }
+        return t;
+    }
+
+    /**
+     * Helper method that will check if argument is an {@link RuntimeException},
+     * and if so, (re)throw it; otherwise just return
+     *
+     * @since 2.9
+     */
+    public static Throwable throwIfRTE(Throwable t) {
+        if (t instanceof RuntimeException) {
+            throw (RuntimeException) t;
+        }
+        return t;
+    }
+
+    /**
+     * Helper method that will check if argument is an {@link IOException},
+     * and if so, (re)throw it; otherwise just return
+     *
+     * @since 2.9
+     */
+    public static Throwable throwIfIOE(Throwable t) throws IOException {
+        if (t instanceof IOException) {
+            throw (IOException) t;
+        }
+        return t;
+    }
+
+    /*
+    /**********************************************************
+    /* Exception handling; other
+    /**********************************************************
+     */
+    
+    /**
      * Method that can be used to find the "root cause", innermost
      * of chained (wrapped) exceptions.
      */
@@ -405,41 +404,21 @@
     }
 
     /**
-     * Method that will unwrap root causes of given Throwable, and throw
-     * the innermost {@link Exception} or {@link Error} as is.
-     * This is useful in cases where mandatory wrapping is added, which
-     * is often done by Reflection API.
-     */
-    public static void throwRootCause(Throwable t) throws Exception
-    {
-        t = getRootCause(t);
-        if (t instanceof Exception) {
-            throw (Exception) t;
-        }
-        throw (Error) t;
-    }
-
-    /**
-     * Method that works like {@link #throwRootCause} if (and only if)
-     * root cause is an {@link IOException}; otherwise returns root cause
+     * Method that works like by calling {@link #getRootCause} and then
+     * either throwing it (if instanceof {@link IOException}), or
+     * return.
      *
      * @since 2.8
      */
-    public static Throwable throwRootCauseIfIOE(Throwable t) throws IOException
-    {
-        t = getRootCause(t);
-        if (t instanceof IOException) {
-            throw (IOException) t;
-        }
-        return t;
+    public static Throwable throwRootCauseIfIOE(Throwable t) throws IOException {
+        return throwIfIOE(getRootCause(t));
     }
 
     /**
      * Method that will wrap 't' as an {@link IllegalArgumentException} if it
      * is a checked exception; otherwise (runtime exception or error) throw as is
      */
-    public static void throwAsIAE(Throwable t)
-    {
+    public static void throwAsIAE(Throwable t) {
         throwAsIAE(t, t.getMessage());
     }
 
@@ -450,16 +429,25 @@
      */
     public static void throwAsIAE(Throwable t, String msg)
     {
-        if (t instanceof RuntimeException) {
-            throw (RuntimeException) t;
-        }
-        if (t instanceof Error) {
-            throw (Error) t;
-        }
+        throwIfRTE(t);
+        throwIfError(t);
         throw new IllegalArgumentException(msg, t);
     }
 
     /**
+     * @since 2.9
+     */
+    public static <T> T throwAsMappingException(DeserializationContext ctxt,
+            IOException e0) throws JsonMappingException {
+        if (e0 instanceof JsonMappingException) {
+            throw (JsonMappingException) e0;
+        }
+        JsonMappingException e = JsonMappingException.from(ctxt, e0.getMessage());
+        e.initCause(e0);
+        throw e;
+    }
+
+    /**
      * Method that will locate the innermost exception for given Throwable;
      * and then wrap it as an {@link IllegalArgumentException} if it
      * is a checked exception; otherwise (runtime exception or error) throw as is
@@ -488,8 +476,8 @@
      *
      * @since 2.8
      */
-    public static void closeOnFailAndThrowAsIAE(JsonGenerator g, Exception fail)
-            throws IOException
+    public static void closeOnFailAndThrowAsIOE(JsonGenerator g, Exception fail)
+        throws IOException
     {
         /* 04-Mar-2014, tatu: Let's try to prevent auto-closing of
          *    structures, which typically causes more damage.
@@ -500,12 +488,8 @@
         } catch (Exception e) {
             fail.addSuppressed(e);
         }
-        if (fail instanceof IOException) {
-            throw (IOException) fail;
-        }
-        if (fail instanceof RuntimeException) {
-            throw (RuntimeException) fail;
-        }
+        throwIfIOE(fail);
+        throwIfRTE(fail);
         throw new RuntimeException(fail);
     }
 
@@ -518,7 +502,7 @@
      *
      * @since 2.8
      */
-    public static void closeOnFailAndThrowAsIAE(JsonGenerator g,
+    public static void closeOnFailAndThrowAsIOE(JsonGenerator g,
             Closeable toClose, Exception fail)
         throws IOException
     {
@@ -537,12 +521,8 @@
                 fail.addSuppressed(e);
             }
         }
-        if (fail instanceof IOException) {
-            throw (IOException) fail;
-        }
-        if (fail instanceof RuntimeException) {
-            throw (RuntimeException) fail;
-        }
+        throwIfIOE(fail);
+        throwIfRTE(fail);
         throw new RuntimeException(fail);
     }
 
@@ -580,17 +560,17 @@
         }
     }
 
-    public static <T> Constructor<T> findConstructor(Class<T> cls, boolean canFixAccess)
+    public static <T> Constructor<T> findConstructor(Class<T> cls, boolean forceAccess)
         throws IllegalArgumentException
     {
         try {
             Constructor<T> ctor = cls.getDeclaredConstructor();
-            if (canFixAccess) {
-                checkAndFixAccess(ctor);
+            if (forceAccess) {
+                checkAndFixAccess(ctor, forceAccess);
             } else {
                 // Has to be public...
                 if (!Modifier.isPublic(ctor.getModifiers())) {
-                    throw new IllegalArgumentException("Default constructor for "+cls.getName()+" is not accessible (non-public?): not allowed to try modify access via Reflection: can not instantiate type");
+                    throw new IllegalArgumentException("Default constructor for "+cls.getName()+" is not accessible (non-public?): not allowed to try modify access via Reflection: cannot instantiate type");
                 }
             }
             return ctor;
@@ -604,6 +584,158 @@
 
     /*
     /**********************************************************
+    /* Class name, description access
+    /**********************************************************
+     */
+
+    /**
+     * @since 2.9
+     */
+    public static Class<?> classOf(Object inst) {
+        if (inst == null) {
+            return null;
+        }
+        return inst.getClass();
+    }
+    
+    /**
+     * @since 2.9
+     */
+    public static Class<?> rawClass(JavaType t) {
+        if (t == null) {
+            return null;
+        }
+        return t.getRawClass();
+    }
+
+    /**
+     * @since 2.9
+     */
+    public static <T> T nonNull(T valueOrNull, T defaultValue) {
+        return (valueOrNull == null) ? defaultValue : valueOrNull;
+    }
+
+    /**
+     * @since 2.9
+     */
+    public static String nullOrToString(Object value) {
+        if (value == null) {
+            return null;
+        }
+        return value.toString();
+    }
+
+    /**
+     * @since 2.9
+     */
+    public static String nonNullString(String str) {
+        if (str == null) {
+            return "";
+        }
+        return str;
+    }
+
+    /**
+     * Returns either quoted value (with double-quotes) -- if argument non-null
+     * String -- or String NULL (no quotes) (if null).
+     *
+     * @since 2.9
+     */
+    public static String quotedOr(Object str, String forNull) {
+        if (str == null) {
+            return forNull;
+        }
+        return String.format("\"%s\"", str);
+    }
+
+    /*
+    /**********************************************************
+    /* Type name, name, desc handling methods
+    /**********************************************************
+     */
+    
+    /**
+     * Helper method used to construct appropriate description
+     * when passed either type (Class) or an instance; in latter
+     * case, class of instance is to be used.
+     */
+    public static String getClassDescription(Object classOrInstance)
+    {
+        if (classOrInstance == null) {
+            return "unknown";
+        }
+        Class<?> cls = (classOrInstance instanceof Class<?>) ?
+            (Class<?>) classOrInstance : classOrInstance.getClass();
+        return nameOf(cls);
+    }
+
+    /**
+     * Helper method used to construct appropriate description
+     * when passed either type (Class) or an instance; in latter
+     * case, class of instance is to be used.
+     *
+     * @since 2.9
+     */
+    public static String classNameOf(Object inst) {
+        if (inst == null) {
+            return "[null]";
+        }
+        return nameOf(inst.getClass());
+    }
+
+    /**
+     * Returns either `cls.getName()` (if `cls` not null),
+     * or "[null]" if `cls` is null.
+     *
+     * @since 2.9
+     */
+    public static String nameOf(Class<?> cls) {
+        if (cls == null) {
+            return "[null]";
+        }
+        int index = 0;
+        while (cls.isArray()) {
+            ++index;
+            cls = cls.getComponentType();
+        }
+        String base = cls.isPrimitive() ? cls.getSimpleName() : cls.getName();
+        if (index > 0) {
+            StringBuilder sb = new StringBuilder(base);
+            do {
+                sb.append("[]");
+            } while (--index > 0);
+            base = sb.toString();
+        }
+        return backticked(base);
+    }
+
+    /**
+     * Returns either backtick-quoted `named.getName()` (if `named` not null),
+     * or "[null]" if `named` is null.
+     *
+     * @since 2.9
+     */
+    public static String nameOf(Named named) {
+        if (named == null) {
+            return "[null]";
+        }
+        return backticked(named.getName());
+    }
+
+    /**
+     * Returns either `text` or [null].
+     *
+     * @since 2.9
+     */
+    public static String backticked(String text) {
+        if (text == null) {
+            return "[null]";
+        }
+        return new StringBuilder(text.length()+2).append('`').append(text).append('`').toString();
+    }
+
+    /*
+    /**********************************************************
     /* Primitive type support
     /**********************************************************
      */
@@ -763,7 +895,7 @@
             // Google App Engine); so let's only fail if we really needed it...
             if (!ao.isAccessible()) {
                 Class<?> declClass = member.getDeclaringClass();
-                throw new IllegalArgumentException("Can not access "+member+" (from class "+declClass.getName()+"; failed to set access: "+se.getMessage());
+                throw new IllegalArgumentException("Cannot access "+member+" (from class "+declClass.getName()+"; failed to set access: "+se.getMessage());
             }
         }
     }
@@ -873,39 +1005,25 @@
     /* Jackson-specific stuff
     /**********************************************************
      */
-    
+
     /**
      * Method that can be called to determine if given Object is the default
      * implementation Jackson uses; as opposed to a custom serializer installed by
      * a module or calling application. Determination is done using
      * {@link JacksonStdImpl} annotation on handler (serializer, deserializer etc)
      * class.
+     *<p>
+     * NOTE: passing `null` is legal, and will result in <code>true</code>
+     * being returned.
      */
     public static boolean isJacksonStdImpl(Object impl) {
-        return (impl != null) && isJacksonStdImpl(impl.getClass());
+        return (impl == null) || isJacksonStdImpl(impl.getClass());
     }
 
     public static boolean isJacksonStdImpl(Class<?> implClass) {
         return (implClass.getAnnotation(JacksonStdImpl.class) != null);
     }
 
-    public static boolean isBogusClass(Class<?> cls) {
-        return (cls == Void.class || cls == Void.TYPE
-                || cls == com.fasterxml.jackson.databind.annotation.NoClass.class);
-    }
-
-    public static boolean isNonStaticInnerClass(Class<?> cls) {
-        return !Modifier.isStatic(cls.getModifiers())
-                && (getEnclosingClass(cls) != null);
-    }
-
-    /**
-     * @since 2.7
-     */
-    public static boolean isObjectOrPrimitive(Class<?> cls) {
-        return (cls == CLS_OBJECT) || cls.isPrimitive();
-    }
-
     /*
     /**********************************************************
     /* Access to various Class definition aspects; possibly
@@ -955,6 +1073,36 @@
     }
 
     /**
+     * Helper method that gets methods declared in given class; usually a simple thing,
+     * but sometimes (as per [databind#785]) more complicated, depending on classloader
+     * setup.
+     *
+     * @since 2.9
+     */
+    public static Method[] getClassMethods(Class<?> cls)
+    {
+        try {
+            return ClassUtil.getDeclaredMethods(cls);
+        } catch (final NoClassDefFoundError ex) {
+            // One of the methods had a class that was not found in the cls.getClassLoader.
+            // Maybe the developer was nice and has a different class loader for this context.
+            final ClassLoader loader = Thread.currentThread().getContextClassLoader();
+            if (loader == null){
+                // Nope... this is going to end poorly
+                throw ex;
+            }
+            final Class<?> contextClass;
+            try {
+                contextClass = loader.loadClass(cls.getName());
+            } catch (ClassNotFoundException e) {
+                ex.addSuppressed(e);
+                throw ex;
+            }
+            return contextClass.getDeclaredMethods(); // Cross fingers
+        }
+    }
+    
+    /**
      * @since 2.7
      */
     public static Ctor[] getConstructors(Class<?> cls) {
@@ -1038,7 +1186,7 @@
             if (enumSetTypeField != null) {
                 return (Class<? extends Enum<?>>) get(set, enumSetTypeField);
             }
-            throw new IllegalStateException("Can not figure out type for EnumSet (odd JDK platform?)");
+            throw new IllegalStateException("Cannot figure out type for EnumSet (odd JDK platform?)");
         }
 
         @SuppressWarnings("unchecked")
@@ -1047,7 +1195,7 @@
             if (enumMapTypeField != null) {
                 return (Class<? extends Enum<?>>) get(set, enumMapTypeField);
             }
-            throw new IllegalStateException("Can not figure out type for EnumMap (odd JDK platform?)");
+            throw new IllegalStateException("Cannot figure out type for EnumMap (odd JDK platform?)");
         }
     	
         private Object get(Object bean, Field field)
@@ -1132,7 +1280,6 @@
             return _ctor.getDeclaringClass();
         }
 
-        // Modest boost: maybe 1%?
         public Annotation[] getDeclaredAnnotations() {
             Annotation[] result = _annotations;
             if (result == null) {
@@ -1142,7 +1289,6 @@
             return result;
         }
 
-        // Modest boost: maybe 1%?
         public  Annotation[][] getParameterAnnotations() {
             Annotation[][] result = _paramAnnotations;
             if (result == null) {
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/CompactStringObjectMap.java b/src/main/java/com/fasterxml/jackson/databind/util/CompactStringObjectMap.java
index 022248b..f27c0c7 100644
--- a/src/main/java/com/fasterxml/jackson/databind/util/CompactStringObjectMap.java
+++ b/src/main/java/com/fasterxml/jackson/databind/util/CompactStringObjectMap.java
@@ -124,6 +124,22 @@
         return null;
     }
 
+    /**
+     * @since 2.9
+     */
+    public Object findCaseInsensitive(String key) {
+        for (int i = 0, end = _hashArea.length; i < end; i += 2) {
+            Object k2 = _hashArea[i];
+            if (k2 != null) {
+                String s = (String) k2;
+                if (s.equalsIgnoreCase(key)) {
+                    return _hashArea[i+1];
+                }
+            }
+        }
+        return null;
+    }
+    
     public List<String> keys() {
         final int end = _hashArea.length;
         List<String> keys = new ArrayList<String>(end >> 2);
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/ConstantValueInstantiator.java b/src/main/java/com/fasterxml/jackson/databind/util/ConstantValueInstantiator.java
new file mode 100644
index 0000000..0f8abb7
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/util/ConstantValueInstantiator.java
@@ -0,0 +1,38 @@
+package com.fasterxml.jackson.databind.util;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.deser.ValueInstantiator;
+
+/**
+ * Trivial {@link ValueInstantiator} implementation that will simply return constant
+ * {@code Object} it is configured with. May be used as-is, or as base class to override
+ * simplistic behavior further.
+ *
+ * @since 2.9.4
+ */
+public class ConstantValueInstantiator extends ValueInstantiator
+{
+    protected final Object _value;
+
+    public ConstantValueInstantiator(Object value) {
+        _value = value;
+    }
+
+    @Override
+    public Class<?> getValueClass() {
+        return _value.getClass();
+    }
+
+    @Override // yes, since default ctor works
+    public boolean canInstantiate() { return true; }
+
+    @Override
+    public boolean canCreateUsingDefault() {  return true; }
+
+    @Override
+    public Object createUsingDefault(DeserializationContext ctxt) throws IOException {
+        return _value;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/Converter.java b/src/main/java/com/fasterxml/jackson/databind/util/Converter.java
index df5dc6a..102a487 100644
--- a/src/main/java/com/fasterxml/jackson/databind/util/Converter.java
+++ b/src/main/java/com/fasterxml/jackson/databind/util/Converter.java
@@ -30,7 +30,7 @@
      * Method that can be used to find out actual input (source) type; this
      * usually can be determined from type parameters, but may need
      * to be implemented differently from programmatically defined
-     * converters (which can not change static type parameter bindings).
+     * converters (which cannot change static type parameter bindings).
      * 
      * @since 2.2
      */
@@ -40,7 +40,7 @@
      * Method that can be used to find out actual output (target) type; this
      * usually can be determined from type parameters, but may need
      * to be implemented differently from programmatically defined
-     * converters (which can not change static type parameter bindings).
+     * converters (which cannot change static type parameter bindings).
      * 
      * @since 2.2
      */
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/EnumResolver.java b/src/main/java/com/fasterxml/jackson/databind/util/EnumResolver.java
index d6ef5e6..f95ea55 100644
--- a/src/main/java/com/fasterxml/jackson/databind/util/EnumResolver.java
+++ b/src/main/java/com/fasterxml/jackson/databind/util/EnumResolver.java
@@ -1,9 +1,9 @@
 package com.fasterxml.jackson.databind.util;
 
-import java.lang.reflect.Method;
 import java.util.*;
 
 import com.fasterxml.jackson.databind.AnnotationIntrospector;
+import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
 
 /**
  * Helper class used to resolve String values (either JSON Object field
@@ -83,17 +83,10 @@
     }
 
     /**
-     * @deprecated Since 2.8, use {@link #constructUsingMethod(Class, Method, AnnotationIntrospector)} instead
+     * @since 2.9
      */
-    @Deprecated
-    public static EnumResolver constructUsingMethod(Class<Enum<?>> enumCls, Method accessor) {
-        return constructUsingMethod(enumCls, accessor, null);
-    }
-
-    /**
-     * @since 2.8
-     */
-    public static EnumResolver constructUsingMethod(Class<Enum<?>> enumCls, Method accessor,
+    public static EnumResolver constructUsingMethod(Class<Enum<?>> enumCls,
+            AnnotatedMember accessor,
             AnnotationIntrospector ai)
     {
         Enum<?>[] enumValues = enumCls.getEnumConstants();
@@ -102,7 +95,7 @@
         for (int i = enumValues.length; --i >= 0; ) {
             Enum<?> en = enumValues[i];
             try {
-                Object o = accessor.invoke(en);
+                Object o = accessor.getValue(en);
                 if (o != null) {
                     map.put(o.toString(), en);
                 }
@@ -129,15 +122,6 @@
     }
 
     /**
-     * @deprecated Since 2.8, use {@link #constructUnsafeUsingToString(Class, AnnotationIntrospector)} instead
-     */
-    @Deprecated
-    public static EnumResolver constructUnsafeUsingToString(Class<?> rawEnumCls)
-    {
-        return constructUnsafeUsingToString(rawEnumCls, null);
-    }
-
-    /**
      * Method that needs to be used instead of {@link #constructUsingToString}
      * if static type of enum is not known.
      *
@@ -153,21 +137,14 @@
     }
 
     /**
-     * @deprecated Since 2.8, use {@link #constructUnsafeUsingMethod(Class, Method, AnnotationIntrospector)} instead.
-     */
-    @Deprecated
-    public static EnumResolver constructUnsafeUsingMethod(Class<?> rawEnumCls, Method accessor) {
-        return constructUnsafeUsingMethod(rawEnumCls, accessor, null);
-    }
-
-    /**
      * Method used when actual String serialization is indicated using @JsonValue
      * on a method.
      *
-     * @since 2.8
+     * @since 2.9
      */
     @SuppressWarnings({ "unchecked" })
-    public static EnumResolver constructUnsafeUsingMethod(Class<?> rawEnumCls, Method accessor,
+    public static EnumResolver constructUnsafeUsingMethod(Class<?> rawEnumCls,
+            AnnotatedMember accessor,
             AnnotationIntrospector ai)
     {            
         // wrong as ever but:
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/EnumValues.java b/src/main/java/com/fasterxml/jackson/databind/util/EnumValues.java
index 6914511..77e1839 100644
--- a/src/main/java/com/fasterxml/jackson/databind/util/EnumValues.java
+++ b/src/main/java/com/fasterxml/jackson/databind/util/EnumValues.java
@@ -46,7 +46,7 @@
         Class<? extends Enum<?>> enumCls = ClassUtil.findEnumType(enumClass);
         Enum<?>[] enumValues = enumCls.getEnumConstants();
         if (enumValues == null) {
-            throw new IllegalArgumentException("Can not determine enum constants for Class "+enumClass.getName());
+            throw new IllegalArgumentException("Cannot determine enum constants for Class "+enumClass.getName());
         }
         String[] names = config.getAnnotationIntrospector().findEnumValues(enumCls, enumValues, new String[enumValues.length]);
         SerializableString[] textual = new SerializableString[enumValues.length];
@@ -72,7 +72,7 @@
             }
             return new EnumValues(enumClass, textual);
         }
-        throw new IllegalArgumentException("Can not determine enum constants for Class "+enumClass.getName());
+        throw new IllegalArgumentException("Cannot determine enum constants for Class "+enumClass.getName());
     }
 
     public SerializableString serializedValueFor(Enum<?> key) {
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/ISO8601DateFormat.java b/src/main/java/com/fasterxml/jackson/databind/util/ISO8601DateFormat.java
index 833ef6e..f36004f 100644
--- a/src/main/java/com/fasterxml/jackson/databind/util/ISO8601DateFormat.java
+++ b/src/main/java/com/fasterxml/jackson/databind/util/ISO8601DateFormat.java
@@ -1,7 +1,6 @@
 package com.fasterxml.jackson.databind.util;
 
 import java.text.*;
-import java.util.Calendar;
 import java.util.Date;
 import java.util.GregorianCalendar;
 
@@ -13,25 +12,21 @@
  *<p>
  * All other methods but parse and format and clone are undefined behavior.
  *
- * @see ISO8601Utils
+ * @deprecated Use {@link com.fasterxml.jackson.databind.util.StdDateFormat} instead
  */
+@Deprecated // since 2.9
 public class ISO8601DateFormat extends DateFormat
 {
     private static final long serialVersionUID = 1L;
 
-    // those classes are to try to allow a consistent behavior for hascode/equals and other methods
-    private static Calendar CALENDAR = new GregorianCalendar();
-    private static NumberFormat NUMBER_FORMAT = new DecimalFormat();
-
     public ISO8601DateFormat() {
-        this.numberFormat = NUMBER_FORMAT;
-        this.calendar = CALENDAR;
+        this.numberFormat = new DecimalFormat();;
+        this.calendar = new GregorianCalendar();;
     }
 
     @Override
     public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) {
-        String value = ISO8601Utils.format(date);
-        toAppendTo.append(value);
+        toAppendTo.append(ISO8601Utils.format(date));
         return toAppendTo;
     }
 
@@ -54,13 +49,6 @@
 
     @Override
     public Object clone() {
-        /* Jackson calls clone for every call. Since this instance is
-         * immutable (and hence thread-safe)
-         * we can just return this instance
-         */
         return this;
     }
-
-    @Override
-    public String toString() { return getClass().getName(); }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/ISO8601Utils.java b/src/main/java/com/fasterxml/jackson/databind/util/ISO8601Utils.java
index b6f30c5..8078317 100644
--- a/src/main/java/com/fasterxml/jackson/databind/util/ISO8601Utils.java
+++ b/src/main/java/com/fasterxml/jackson/databind/util/ISO8601Utils.java
@@ -12,54 +12,16 @@
  * 
  * @see <a href="http://www.w3.org/TR/NOTE-datetime">this specification</a>
  */
+@Deprecated // since 2.9
 public class ISO8601Utils
 {
-    @Deprecated // since 2.7
-    private static final String GMT_ID = "GMT";
-
-    /**
-     * ID to represent the 'UTC' string, default timezone since Jackson 2.7
-     * 
-     * @since 2.7
-     */
-    private static final String UTC_ID = "UTC";
-    
-    /**
-     * The GMT timezone, prefetched to avoid more lookups.
-     * 
-     * @deprecated Since 2.7 use {@link #TIMEZONE_UTC} instead
-     */
-    @Deprecated
-    private static final TimeZone TIMEZONE_GMT = TimeZone.getTimeZone(GMT_ID);
-
-    /**
-     * The UTC timezone, prefetched to avoid more lookups.
-     * 
-     * @since 2.7
-     */
-    private static final TimeZone TIMEZONE_UTC = TimeZone.getTimeZone(UTC_ID);
+    protected final static int DEF_8601_LEN = "yyyy-MM-ddThh:mm:ss.SSS+00:00".length();
 
     /**
      * Timezone we use for 'Z' in ISO-8601 date/time values: since 2.7
      * {@link #TIMEZONE_UTC}; with earlier versions up to 2.7 was {@link #TIMEZONE_GMT}.
      */
-    private static final TimeZone TIMEZONE_Z = TIMEZONE_UTC;
-    
-    /*
-    /**********************************************************
-    /* Static factories
-    /**********************************************************
-     */
-
-    /**
-     * Accessor for static GMT timezone instance.
-     *
-     * @deprecated since 2.6
-     */
-    @Deprecated // since 2.6
-    public static TimeZone timeZoneGMT() {
-        return TIMEZONE_GMT;
-    }
+    private static final TimeZone TIMEZONE_Z = TimeZone.getTimeZone("UTC");
 
     /*
     /**********************************************************
@@ -74,7 +36,7 @@
      * @return the date formatted as 'yyyy-MM-ddThh:mm:ssZ'
      */
     public static String format(Date date) {
-        return format(date, false, TIMEZONE_UTC);
+        return format(date, false, TIMEZONE_Z);
     }
 
     /**
@@ -85,7 +47,12 @@
      * @return the date formatted as 'yyyy-MM-ddThh:mm:ss[.sss]Z'
      */
     public static String format(Date date, boolean millis) {
-        return format(date, millis, TIMEZONE_UTC);
+        return format(date, millis, TIMEZONE_Z);
+    }
+
+    @Deprecated // since 2.9
+    public static String format(Date date, boolean millis, TimeZone tz) {
+        return format(date, millis, tz, Locale.US);
     }
 
     /**
@@ -95,46 +62,39 @@
      * @param millis true to include millis precision otherwise false
      * @param tz timezone to use for the formatting (UTC will produce 'Z')
      * @return the date formatted as yyyy-MM-ddThh:mm:ss[.sss][Z|[+-]hh:mm]
+     *
+     * @since 2.9
      */
-    public static String format(Date date, boolean millis, TimeZone tz) {
-        Calendar calendar = new GregorianCalendar(tz, Locale.US);
+    public static String format(Date date, boolean millis, TimeZone tz, Locale loc) {
+        Calendar calendar = new GregorianCalendar(tz, loc);
         calendar.setTime(date);
 
         // estimate capacity of buffer as close as we can (yeah, that's pedantic ;)
-        int capacity = "yyyy-MM-ddThh:mm:ss".length();
-        capacity += millis ? ".sss".length() : 0;
-        capacity += tz.getRawOffset() == 0 ? "Z".length() : "+hh:mm".length();
-        StringBuilder formatted = new StringBuilder(capacity);
-
-        padInt(formatted, calendar.get(Calendar.YEAR), "yyyy".length());
-        formatted.append('-');
-        padInt(formatted, calendar.get(Calendar.MONTH) + 1, "MM".length());
-        formatted.append('-');
-        padInt(formatted, calendar.get(Calendar.DAY_OF_MONTH), "dd".length());
-        formatted.append('T');
-        padInt(formatted, calendar.get(Calendar.HOUR_OF_DAY), "hh".length());
-        formatted.append(':');
-        padInt(formatted, calendar.get(Calendar.MINUTE), "mm".length());
-        formatted.append(':');
-        padInt(formatted, calendar.get(Calendar.SECOND), "ss".length());
+        StringBuilder sb = new StringBuilder(30);
+        sb.append(String.format(
+                "%04d-%02d-%02dT%02d:%02d:%02d",
+                calendar.get(Calendar.YEAR),
+                calendar.get(Calendar.MONTH) + 1,
+                calendar.get(Calendar.DAY_OF_MONTH),
+                calendar.get(Calendar.HOUR_OF_DAY),
+                calendar.get(Calendar.MINUTE),
+                calendar.get(Calendar.SECOND)
+                ));
         if (millis) {
-            formatted.append('.');
-            padInt(formatted, calendar.get(Calendar.MILLISECOND), "sss".length());
+            sb.append(String.format(".%03d", calendar.get(Calendar.MILLISECOND)));
         }
 
         int offset = tz.getOffset(calendar.getTimeInMillis());
         if (offset != 0) {
             int hours = Math.abs((offset / (60 * 1000)) / 60);
             int minutes = Math.abs((offset / (60 * 1000)) % 60);
-            formatted.append(offset < 0 ? '-' : '+');
-            padInt(formatted, hours, "hh".length());
-            formatted.append(':');
-            padInt(formatted, minutes, "mm".length());
+            sb.append(String.format("%c%02d:%02d",
+                    (offset < 0 ? '-' : '+'),
+                    hours, minutes));
         } else {
-            formatted.append('Z');
+            sb.append('Z');
         }
-
-        return formatted.toString();
+        return sb.toString();
     }
 
     /*
@@ -287,11 +247,7 @@
             return calendar.getTime();
             // If we get a ParseException it'll already have the right message/offset.
             // Other exception types can convert here.
-        } catch (IndexOutOfBoundsException e) {
-            fail = e;
-        } catch (NumberFormatException e) {
-            fail = e;
-        } catch (IllegalArgumentException e) {
+        } catch (Exception e) {
             fail = e;
         }
         String input = (date == null) ? null : ('"' + date + '"');
@@ -352,21 +308,6 @@
     }
 
     /**
-     * Zero pad a number to a specified length
-     * 
-     * @param buffer buffer to use for padding
-     * @param value the integer value to pad if necessary.
-     * @param length the length of the string we should zero pad
-     */
-    private static void padInt(StringBuilder buffer, int value, int length) {
-        String strValue = Integer.toString(value);
-        for (int i = length - strValue.length(); i > 0; i--) {
-            buffer.append('0');
-        }
-        buffer.append(strValue);
-    }
-
-    /**
      * Returns the index of the first character in the string that is not a digit, starting at offset.
      */
     private static int indexOfNonDigit(String string, int offset) {
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/NameTransformer.java b/src/main/java/com/fasterxml/jackson/databind/util/NameTransformer.java
index 646fb87..b190968 100644
--- a/src/main/java/com/fasterxml/jackson/databind/util/NameTransformer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/util/NameTransformer.java
@@ -108,7 +108,7 @@
 
     /**
      * Method called when reversal of transformation is needed; should return
-     * null if this is not possible, that is, given name can not have been
+     * null if this is not possible, that is, given name cannot have been
      * result of calling {@link #transform} of this object.
      */
     public abstract String reverse(String transformed);
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/ObjectBuffer.java b/src/main/java/com/fasterxml/jackson/databind/util/ObjectBuffer.java
index f04f816..034e487 100644
--- a/src/main/java/com/fasterxml/jackson/databind/util/ObjectBuffer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/util/ObjectBuffer.java
@@ -5,7 +5,7 @@
 
 /**
  * Helper class to use for constructing Object arrays by appending entries
- * to create arrays of various lengths (length that is not known a priori). 
+ * to create arrays of various lengths (length that is not known a priori).
  */
 public final class ObjectBuffer
 {
@@ -67,12 +67,25 @@
     {
         _reset();
         if (_freeBuffer == null) {
-            return new Object[12];
+            return (_freeBuffer = new Object[12]);
         }
         return _freeBuffer;
     }
 
     /**
+     * @since 2.9
+     */
+    public Object[] resetAndStart(Object[] base, int count)
+    {
+        _reset();
+        if ((_freeBuffer == null) || (_freeBuffer.length < count)) {
+            _freeBuffer = new Object[Math.max(12, count)];
+        }
+        System.arraycopy(base, 0, _freeBuffer, 0, count);
+        return _freeBuffer;
+    }
+
+    /**
      * Method called to add a full Object array as a chunk buffered within
      * this buffer, and to obtain a new array to fill. Caller is not to use
      * the array it gives; but to use the returned array for continued
@@ -121,6 +134,7 @@
         int totalSize = lastChunkEntries + _size;
         Object[] result = new Object[totalSize];
         _copyTo(result, totalSize, lastChunk, lastChunkEntries);
+        _reset();
         return result;
     }
 
@@ -154,6 +168,7 @@
         for (int i = 0; i < lastChunkEntries; ++i) {
             resultList.add(lastChunk[i]);
         }
+        _reset();
     }
     
     /**
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/RawValue.java b/src/main/java/com/fasterxml/jackson/databind/util/RawValue.java
index b9f1b02..aea4427 100644
--- a/src/main/java/com/fasterxml/jackson/databind/util/RawValue.java
+++ b/src/main/java/com/fasterxml/jackson/databind/util/RawValue.java
@@ -119,7 +119,6 @@
     
     @Override
     public String toString() {
-        return String.format("[RawValue of type %s]",
-                (_value == null) ? "NULL" : _value.getClass().getName());
+        return String.format("[RawValue of type %s]", ClassUtil.classNameOf(_value));
     }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/SimpleBeanPropertyDefinition.java b/src/main/java/com/fasterxml/jackson/databind/util/SimpleBeanPropertyDefinition.java
index 575ad68..cd9cdf2 100644
--- a/src/main/java/com/fasterxml/jackson/databind/util/SimpleBeanPropertyDefinition.java
+++ b/src/main/java/com/fasterxml/jackson/databind/util/SimpleBeanPropertyDefinition.java
@@ -4,11 +4,11 @@
 import java.util.Iterator;
 
 import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.databind.AnnotationIntrospector;
-import com.fasterxml.jackson.databind.PropertyMetadata;
-import com.fasterxml.jackson.databind.PropertyName;
+
+import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.cfg.MapperConfig;
 import com.fasterxml.jackson.databind.introspect.*;
+import com.fasterxml.jackson.databind.type.TypeFactory;
 
 /**
  * Simple immutable {@link BeanPropertyDefinition} implementation that can
@@ -19,7 +19,7 @@
 public class SimpleBeanPropertyDefinition
     extends BeanPropertyDefinition
 {
-    protected final AnnotationIntrospector _introspector;
+    protected final AnnotationIntrospector _annotationIntrospector;
 
     /**
      * Member that defines logical property. Assumption is that it
@@ -45,67 +45,34 @@
      */
     protected final JsonInclude.Value _inclusion;
 
-    /**
-     * @deprecated Since 2.5 use <code>_fullName</code> instead.
-     */
-    @Deprecated
-    protected final String _name;
-
     /*
     /**********************************************************
     /* Construction
     /**********************************************************
      */
 
-    protected SimpleBeanPropertyDefinition(AnnotatedMember member, PropertyName fullName,
-            AnnotationIntrospector intr, PropertyMetadata metadata,
-            JsonInclude.Include inclusion)
-    {
-        this(member, fullName, intr, metadata,
-                ((inclusion == null) || (inclusion == JsonInclude.Include.USE_DEFAULTS)
-                        ? EMPTY_INCLUDE : JsonInclude.Value.construct(inclusion, null)));
-    }
-    
-    protected SimpleBeanPropertyDefinition(AnnotatedMember member, PropertyName fullName,
-            AnnotationIntrospector intr, PropertyMetadata metadata,
+    /**
+     * @since 2.9
+     */
+    protected SimpleBeanPropertyDefinition(AnnotationIntrospector intr,
+            AnnotatedMember member, PropertyName fullName, PropertyMetadata metadata,
             JsonInclude.Value inclusion)
     {
-        _introspector = intr;
+        _annotationIntrospector = intr;
         _member = member;
         _fullName = fullName;
-        _name = fullName.getSimpleName();
         _metadata = (metadata == null) ? PropertyMetadata.STD_OPTIONAL: metadata;
         _inclusion = inclusion;
     }
 
     /**
-     * @deprecated Since 2.5 Use variant that takes PropertyName
-     */
-    @Deprecated
-    protected SimpleBeanPropertyDefinition(AnnotatedMember member, String name,
-    		AnnotationIntrospector intr) {
-        this(member, new PropertyName(name), intr, null, EMPTY_INCLUDE);
-    }
-
-    /**
      * @since 2.2
      */
     public static SimpleBeanPropertyDefinition construct(MapperConfig<?> config,
-    		AnnotatedMember member) {
-        return new SimpleBeanPropertyDefinition(member, PropertyName.construct(member.getName()),
-                (config == null) ? null : config.getAnnotationIntrospector(),
-                        null, EMPTY_INCLUDE);
-    }
-
-    /**
-     * @deprecated Since 2.5
-     */
-    @Deprecated
-    public static SimpleBeanPropertyDefinition construct(MapperConfig<?> config,
-    		AnnotatedMember member, String name) {
-        return new SimpleBeanPropertyDefinition(member, PropertyName.construct(name),
-                (config == null) ? null : config.getAnnotationIntrospector(),
-                        null, EMPTY_INCLUDE);
+    		AnnotatedMember member)
+    {
+        return new SimpleBeanPropertyDefinition(config.getAnnotationIntrospector(),
+                member, PropertyName.construct(member.getName()), null, EMPTY_INCLUDE);
     }
 
     /**
@@ -117,14 +84,19 @@
     }
 
     /**
+     * Method called to create instance for virtual properties.
+     *
      * @since 2.5
      */
     public static SimpleBeanPropertyDefinition construct(MapperConfig<?> config,
             AnnotatedMember member, PropertyName name, PropertyMetadata metadata,
-            JsonInclude.Include inclusion) {
-          return new SimpleBeanPropertyDefinition(member, name,
-                  (config == null) ? null : config.getAnnotationIntrospector(),
-                          metadata, inclusion);
+            JsonInclude.Include inclusion)
+    {
+        JsonInclude.Value inclValue
+             = ((inclusion == null) || (inclusion == JsonInclude.Include.USE_DEFAULTS)) 
+             ? EMPTY_INCLUDE : JsonInclude.Value.construct(inclusion, null);
+        return new SimpleBeanPropertyDefinition(config.getAnnotationIntrospector(),
+                member, name, metadata, inclValue);
     }
     
     /**
@@ -133,9 +105,8 @@
     public static SimpleBeanPropertyDefinition construct(MapperConfig<?> config,
             AnnotatedMember member, PropertyName name, PropertyMetadata metadata,
             JsonInclude.Value inclusion) {
-          return new SimpleBeanPropertyDefinition(member, name,
-                  (config == null) ? null : config.getAnnotationIntrospector(),
-                          metadata, inclusion);
+          return new SimpleBeanPropertyDefinition(config.getAnnotationIntrospector(),
+                  member, name, metadata, inclusion);
     }
 
     /*
@@ -144,19 +115,13 @@
     /**********************************************************
      */
 
-    // Note: removed from base class in 2.6; left here until 2.7
-    @Deprecated // since 2.3 (remove in 2.7)
-    public BeanPropertyDefinition withName(String newName) {
-        return withSimpleName(newName);
-    }
-
     @Override
     public BeanPropertyDefinition withSimpleName(String newName) {
         if (_fullName.hasSimpleName(newName) && !_fullName.hasNamespace()) {
             return this;
         }
-        return new SimpleBeanPropertyDefinition(_member, new PropertyName(newName),
-                _introspector, _metadata, _inclusion);
+        return new SimpleBeanPropertyDefinition(_annotationIntrospector,
+                _member, new PropertyName(newName), _metadata, _inclusion);
     }
 
     @Override
@@ -164,8 +129,8 @@
         if (_fullName.equals(newName)) {
             return this;
         }
-        return new SimpleBeanPropertyDefinition(_member, newName,
-                _introspector, _metadata, _inclusion);
+        return new SimpleBeanPropertyDefinition(_annotationIntrospector, 
+                _member, newName, _metadata, _inclusion);
     }
 
     /**
@@ -175,8 +140,8 @@
         if (metadata.equals(_metadata)) {
             return this;
         }
-        return new SimpleBeanPropertyDefinition(_member, _fullName,
-                _introspector, metadata, _inclusion);
+        return new SimpleBeanPropertyDefinition(_annotationIntrospector,
+                _member, _fullName, metadata, _inclusion);
     }
 
     /**
@@ -186,8 +151,8 @@
         if (_inclusion == inclusion) {
             return this;
         }
-        return new SimpleBeanPropertyDefinition(_member, _fullName,
-                _introspector, _metadata, inclusion);
+        return new SimpleBeanPropertyDefinition(_annotationIntrospector,
+                _member, _fullName, _metadata, inclusion);
     }
 
     /*
@@ -212,8 +177,10 @@
 
     @Override
     public PropertyName getWrapperName() {
-        return ((_introspector == null) && (_member != null))
-                ? null : _introspector.findWrapperName(_member);
+        if ((_annotationIntrospector == null) || (_member == null)) {
+            return null;
+        }
+        return _annotationIntrospector.findWrapperName(_member);
     }
 
     // hmmh. what should we claim here?
@@ -231,6 +198,22 @@
     }
 
     @Override
+    public JavaType getPrimaryType() {
+        if (_member == null) {
+            return TypeFactory.unknownType();
+        }
+        return _member.getType();
+    }
+
+    @Override
+    public Class<?> getRawPrimaryType() {
+        if (_member == null) {
+            return Object.class;
+        }
+        return _member.getRawType();
+    }
+
+    @Override
     public JsonInclude.Value findInclusion() {
         return _inclusion;
     }
@@ -290,46 +273,6 @@
         return Collections.singleton(param).iterator();
     }
 
-    /**
-     * Method used to find accessor (getter, field to access) to use for accessing
-     * value of the property.
-     * Null if no such member exists.
-     */
-    @Override
-    public AnnotatedMember getAccessor() {
-        AnnotatedMember acc = getGetter();
-        if (acc == null) {
-            acc = getField();
-        }
-        return acc;
-    }
-
-    /**
-     * Method used to find mutator (constructor parameter, setter, field) to use for
-     * changing value of the property.
-     * Null if no such member exists.
-     */
-    @Override
-    public AnnotatedMember getMutator() {
-        AnnotatedMember acc = getConstructorParameter();
-        if (acc == null) {
-            acc = getSetter();
-            if (acc == null) {
-                acc = getField();
-            }
-        }
-        return acc;
-    }
-
-    @Override
-    public AnnotatedMember getNonConstructorMutator() {
-        AnnotatedMember acc = getSetter();
-        if (acc == null) {
-            acc = getField();
-        }
-        return acc;
-    }
-
     @Override
     public AnnotatedMember getPrimaryMember() { return _member; }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/StdConverter.java b/src/main/java/com/fasterxml/jackson/databind/util/StdConverter.java
index cd2b2cb..1bc3f02 100644
--- a/src/main/java/com/fasterxml/jackson/databind/util/StdConverter.java
+++ b/src/main/java/com/fasterxml/jackson/databind/util/StdConverter.java
@@ -36,7 +36,7 @@
         JavaType thisType = tf.constructType(getClass());
         JavaType convType = thisType.findSuperType(Converter.class);
         if (convType == null || convType.containedTypeCount() < 2) {
-            throw new IllegalStateException("Can not find OUT type parameter for Converter of type "+getClass().getName());
+            throw new IllegalStateException("Cannot find OUT type parameter for Converter of type "+getClass().getName());
         }
         return convType;
     }
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/StdDateFormat.java b/src/main/java/com/fasterxml/jackson/databind/util/StdDateFormat.java
index 46346de..57a0787 100644
--- a/src/main/java/com/fasterxml/jackson/databind/util/StdDateFormat.java
+++ b/src/main/java/com/fasterxml/jackson/databind/util/StdDateFormat.java
@@ -6,6 +6,8 @@
 import java.text.ParsePosition;
 import java.text.SimpleDateFormat;
 import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import com.fasterxml.jackson.core.io.NumberInput;
 
@@ -19,41 +21,46 @@
 public class StdDateFormat
     extends DateFormat
 {
-    /* TODO !!! 24-Nov-2009, tatu: Should rewrite this class:
-     * JDK date parsing is awfully brittle, and ISO-8601 is quite
-     * permissive. The two don't mix, need to write a better one.
+    /* 24-Jun-2017, tatu: Finally rewrote deserialization to use basic Regex
+     *   instead of SimpleDateFormat; partly for better concurrency, partly
+     *   for easier enforcing of specific rules. Heavy lifting done by Calendar,
+     *   anyway.
      */
-    // 02-Oct-2014, tatu: Alas. While spit'n'polished a few times, still
-    //   not really robust. But still in use.
+    protected final static String PATTERN_PLAIN_STR = "\\d\\d\\d\\d[-]\\d\\d[-]\\d\\d";
+
+    protected final static Pattern PATTERN_PLAIN = Pattern.compile(PATTERN_PLAIN_STR);
+
+    protected final static Pattern PATTERN_ISO8601;
+    static {
+        Pattern p = null;
+        try {
+            p = Pattern.compile(PATTERN_PLAIN_STR
+                    +"[T]\\d\\d[:]\\d\\d(?:[:]\\d\\d)?" // hours, minutes, optional seconds
+                    +"(\\.\\d+)?" // optional second fractions
+                    +"(Z|[+-]\\d\\d(?:[:]?\\d\\d)?)?" // optional timeoffset/Z
+            );
+        } catch (Throwable t) {
+            throw new RuntimeException(t);
+        }
+        PATTERN_ISO8601 = p;
+    }
 
     /**
      * Defines a commonly used date format that conforms
      * to ISO-8601 date formatting standard, when it includes basic undecorated
-     * timezone definition
+     * timezone definition.
      */
     public final static String DATE_FORMAT_STR_ISO8601 = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
 
     /**
-     * Same as 'regular' 8601, but handles 'Z' as an alias for "+0000"
-     * (or "UTC")
-     */
-    protected final static String DATE_FORMAT_STR_ISO8601_Z = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
-
-    /**
-     * Same as 'regular' 8601 except misses timezone altogether
-     *
-     * @since 2.8.10
-     */
-    protected final static String DATE_FORMAT_STR_ISO8601_NO_TZ = "yyyy-MM-dd'T'HH:mm:ss.SSS";
-
-    /**
-     * ISO-8601 with just the Date part, no time
+     * ISO-8601 with just the Date part, no time: needed for error messages
      */
     protected final static String DATE_FORMAT_STR_PLAIN = "yyyy-MM-dd";
 
     /**
      * This constant defines the date format specified by
-     * RFC 1123 / RFC 822.
+     * RFC 1123 / RFC 822. Used for parsing via `SimpleDateFormat` as well as
+     * error messages.
      */
     protected final static String DATE_FORMAT_STR_RFC1123 = "EEE, dd MMM yyyy HH:mm:ss zzz";
 
@@ -62,8 +69,7 @@
      */
     protected final static String[] ALL_FORMATS = new String[] {
         DATE_FORMAT_STR_ISO8601,
-        DATE_FORMAT_STR_ISO8601_Z,
-        DATE_FORMAT_STR_ISO8601_NO_TZ,
+        "yyyy-MM-dd'T'HH:mm:ss.SSS", // ISO-8601 but no timezone
         DATE_FORMAT_STR_RFC1123,
         DATE_FORMAT_STR_PLAIN
     };
@@ -72,47 +78,44 @@
      * By default we use UTC for everything, with Jackson 2.7 and later
      * (2.6 and earlier relied on GMT)
      */
-    private final static TimeZone DEFAULT_TIMEZONE;
+    protected final static TimeZone DEFAULT_TIMEZONE;
     static {
         DEFAULT_TIMEZONE = TimeZone.getTimeZone("UTC"); // since 2.7
     }
 
-    private final static Locale DEFAULT_LOCALE = Locale.US;
+    protected final static Locale DEFAULT_LOCALE = Locale.US;
 
     protected final static DateFormat DATE_FORMAT_RFC1123;
 
     protected final static DateFormat DATE_FORMAT_ISO8601;
-    protected final static DateFormat DATE_FORMAT_ISO8601_Z;
-    protected final static DateFormat DATE_FORMAT_ISO8601_NO_TZ; // since 2.8.10
 
-    protected final static DateFormat DATE_FORMAT_PLAIN;
-
-    /* Let's construct "blueprint" date format instances: can not be used
+    /* Let's construct "blueprint" date format instances: cannot be used
      * as is, due to thread-safety issues, but can be used for constructing
      * actual instances more cheaply (avoids re-parsing).
      */
     static {
-        /* Another important thing: let's force use of default timezone for
-         * baseline DataFormat objects
-         */
-
+        // Another important thing: let's force use of default timezone for
+        // baseline DataFormat objects
         DATE_FORMAT_RFC1123 = new SimpleDateFormat(DATE_FORMAT_STR_RFC1123, DEFAULT_LOCALE);
         DATE_FORMAT_RFC1123.setTimeZone(DEFAULT_TIMEZONE);
         DATE_FORMAT_ISO8601 = new SimpleDateFormat(DATE_FORMAT_STR_ISO8601, DEFAULT_LOCALE);
         DATE_FORMAT_ISO8601.setTimeZone(DEFAULT_TIMEZONE);
-        DATE_FORMAT_ISO8601_Z = new SimpleDateFormat(DATE_FORMAT_STR_ISO8601_Z, DEFAULT_LOCALE);
-        DATE_FORMAT_ISO8601_Z.setTimeZone(DEFAULT_TIMEZONE);
-        DATE_FORMAT_ISO8601_NO_TZ = new SimpleDateFormat(DATE_FORMAT_STR_ISO8601_NO_TZ, DEFAULT_LOCALE);
-        DATE_FORMAT_ISO8601_NO_TZ.setTimeZone(DEFAULT_TIMEZONE);
-        DATE_FORMAT_PLAIN = new SimpleDateFormat(DATE_FORMAT_STR_PLAIN, DEFAULT_LOCALE);
-        DATE_FORMAT_PLAIN.setTimeZone(DEFAULT_TIMEZONE);
     }
-    
+
     /**
      * A singleton instance can be used for cloning purposes, as a blueprint of sorts.
      */
     public final static StdDateFormat instance = new StdDateFormat();
-    
+
+    /**
+     * Blueprint "Calendar" instance for use during formatting. Cannot be used as is,
+     * due to thread-safety issues, but can be used for constructing actual instances 
+     * more cheaply by cloning.
+     *
+     * @since 2.9.1
+     */
+    protected static final Calendar CALENDAR = new GregorianCalendar(DEFAULT_TIMEZONE, DEFAULT_LOCALE);
+
     /**
      * Caller may want to explicitly override timezone to use; if so,
      * we will have non-null value here.
@@ -124,19 +127,29 @@
     /**
      * Explicit override for leniency, if specified.
      *<p>
-     * Can not be `final` because {@link #setLenient(boolean)} returns
+     * Cannot be `final` because {@link #setLenient(boolean)} returns
      * `void`.
      *
      * @since 2.7
      */
     protected Boolean _lenient;
-    
-    protected transient DateFormat _formatRFC1123;
-    protected transient DateFormat _formatISO8601;
-    protected transient DateFormat _formatISO8601_z;
-    protected transient DateFormat _formatISO8601_noTz; // 2.8.10
-    protected transient DateFormat _formatPlain;
 
+    /**
+     * Lazily instantiated calendar used by this instance for serialization ({@link #format(Date)}).
+     *
+     * @since 2.9.1
+     */
+    private transient Calendar _calendar;
+    
+    private transient DateFormat _formatRFC1123;
+
+    /** 
+     * Whether the TZ offset must be formatted with a colon between hours and minutes ({@code HH:mm} format)
+     *
+     * @since 2.9.1
+     */
+    private boolean _tzSerializedWithColon = false;
+    
     /*
     /**********************************************************
     /* Life cycle, accessing singleton "standard" formats
@@ -154,9 +167,18 @@
     }
 
     protected StdDateFormat(TimeZone tz, Locale loc, Boolean lenient) {
+        this(tz, loc, lenient, false);
+    }
+
+    /**
+     * @since 2.9.1
+     */
+    protected StdDateFormat(TimeZone tz, Locale loc, Boolean lenient,
+            boolean formatTzOffsetWithColon) {
         _timezone = tz;
         _locale = loc;
         _lenient = lenient;
+        _tzSerializedWithColon = formatTzOffsetWithColon;
     }
     
     public static TimeZone getDefaultTimeZone() {
@@ -174,30 +196,61 @@
         if ((tz == _timezone) || tz.equals(_timezone)) {
             return this;
         }
-        return new StdDateFormat(tz, _locale, _lenient);
+        return new StdDateFormat(tz, _locale, _lenient, _tzSerializedWithColon);
     }
 
+    /**
+     * "Mutant factory" method that will return an instance that uses specified
+     * {@code Locale}:
+     * either {@code this} instance (if setting would not change), or newly
+     * constructed instance with different {@code Locale} to use.
+     */
     public StdDateFormat withLocale(Locale loc) {
         if (loc.equals(_locale)) {
             return this;
         }
-        return new StdDateFormat(_timezone, loc, _lenient);
-    }
-    
-    @Override
-    public StdDateFormat clone() {
-        /* Although there is that much state to share, we do need to
-         * orchestrate a bit, mostly since timezones may be changed
-         */
-        return new StdDateFormat(_timezone, _locale, _lenient);
+        return new StdDateFormat(_timezone, loc, _lenient, _tzSerializedWithColon);
     }
 
     /**
-     * @deprecated Since 2.4; use variant that takes Locale
+     * "Mutant factory" method that will return an instance that has specified leniency
+     * setting: either {@code this} instance (if setting would not change), or newly
+     * constructed instance.
+     *
+     * @since 2.9
      */
-    @Deprecated
-    public static DateFormat getISO8601Format(TimeZone tz) {
-        return getISO8601Format(tz, DEFAULT_LOCALE);
+    public StdDateFormat withLenient(Boolean b) {
+        if (_equals(b, _lenient)) {
+            return this;
+        }
+        return new StdDateFormat(_timezone, _locale, b, _tzSerializedWithColon);
+    }
+
+    /**
+     * "Mutant factory" method that will return an instance that has specified
+     * handling of colon when serializing timezone (timezone either written
+     * like {@code +0500} or {@code +05:00}):
+     * either {@code this} instance (if setting would not change), or newly
+     * constructed instance with desired setting for colon inclusion.
+     *<p>
+     * NOTE: does NOT affect deserialization as colon is optional accepted
+     * but not required -- put another way, either serialization is accepted
+     * by this class.
+     *
+     * @since 2.9.1
+     */
+    public StdDateFormat withColonInTimeZone(boolean b) {
+        if (_tzSerializedWithColon == b) {
+            return this;
+        }
+        return new StdDateFormat(_timezone, _locale, _lenient, b);
+     }
+    
+    @Override
+    public StdDateFormat clone() {
+        // Although there is that much state to share, we do need to
+        // orchestrate a bit, mostly since timezones may be changed
+        return new StdDateFormat(_timezone, _locale, _lenient, _tzSerializedWithColon);
     }
 
     /**
@@ -206,7 +259,10 @@
      * compliant date format.
      * 
      * @since 2.4
+     *
+     * @deprecated Since 2.9
      */
+    @Deprecated // since 2.9
     public static DateFormat getISO8601Format(TimeZone tz, Locale loc) {
         return _cloneFormat(DATE_FORMAT_ISO8601, DATE_FORMAT_STR_ISO8601, tz, loc, null);
     }
@@ -217,20 +273,15 @@
      * compliant date format.
      * 
      * @since 2.4
+     *
+     * @deprecated Since 2.9
      */
+    @Deprecated // since 2.9
     public static DateFormat getRFC1123Format(TimeZone tz, Locale loc) {
         return _cloneFormat(DATE_FORMAT_RFC1123, DATE_FORMAT_STR_RFC1123,
                 tz, loc, null);
     }
 
-    /**
-     * @deprecated Since 2.4; use variant that takes Locale
-     */
-    @Deprecated
-    public static DateFormat getRFC1123Format(TimeZone tz) {
-        return getRFC1123Format(tz, DEFAULT_LOCALE);
-    }
-
     /*
     /**********************************************************
     /* Public API, configuration
@@ -261,8 +312,8 @@
      */
     @Override // since 2.7
     public void setLenient(boolean enabled) {
-        Boolean newValue = enabled;
-        if (_lenient != newValue) {
+        Boolean newValue = Boolean.valueOf(enabled);
+        if (!_equals(newValue, _lenient)) {
             _lenient = newValue;
             // and since leniency settings may have been used:
             _clearFormats();
@@ -271,11 +322,26 @@
 
     @Override // since 2.7
     public boolean isLenient() {
-        if (_lenient == null) {
-            // default is, I believe, true
-            return true;
-        }
-        return _lenient.booleanValue();
+        // default is, I believe, true
+        return (_lenient == null) || _lenient.booleanValue();
+    }
+
+    /**
+     * Accessor for checking whether this instance would include colon
+     * within timezone serialization or not: if {code true}, timezone offset
+     * is serialized like {@code -06:00}; if {code false} as {@code -0600}.
+     *<p>
+     * NOTE: only relevant for serialization (formatting), as deserialization
+     * (parsing) always accepts optional colon but does not require it, regardless
+     * of this setting.
+     *
+     * @return {@code true} if a colon is to be inserted between the hours and minutes 
+     * of the TZ offset when serializing as String; otherwise {@code false}
+     *
+     * @since 2.9.1
+     */
+    public boolean isColonIncludedInTimeZone() {
+        return _tzSerializedWithColon;
     }
 
     /*
@@ -289,36 +355,10 @@
     {
         dateStr = dateStr.trim();
         ParsePosition pos = new ParsePosition(0);
-
-        Date dt;
-
-        if (looksLikeISO8601(dateStr)) { // also includes "plain"
-            dt = parseAsISO8601(dateStr, pos, true);
-        } else {
-            // Also consider "stringified" simple time stamp
-            int i = dateStr.length();
-            while (--i >= 0) {
-                char ch = dateStr.charAt(i);
-                if (ch < '0' || ch > '9') {
-                    // 07-Aug-2013, tatu: And [databind#267] points out that negative numbers should also work
-                    if (i > 0 || ch != '-') {
-                        break;
-                    }
-                }
-            }
-            if ((i < 0)
-                // let's just assume negative numbers are fine (can't be RFC-1123 anyway); check length for positive
-                    && (dateStr.charAt(0) == '-' || NumberInput.inLongRange(dateStr, false))) {
-                dt = new Date(Long.parseLong(dateStr));
-            } else {
-                // Otherwise, fall back to using RFC 1123
-                dt = parseAsRFC1123(dateStr, pos);
-            }
-        }
+        Date dt = _parseDate(dateStr, pos);
         if (dt != null) {
             return dt;
         }
-
         StringBuilder sb = new StringBuilder();
         for (String f : ALL_FORMATS) {
             if (sb.length() > 0) {
@@ -330,19 +370,26 @@
         }
         sb.append('"');
         throw new ParseException
-            (String.format("Can not parse date \"%s\": not compatible with any of standard forms (%s)",
+            (String.format("Cannot parse date \"%s\": not compatible with any of standard forms (%s)",
                            dateStr, sb.toString()), pos.getErrorIndex());
     }
 
+    // 24-Jun-2017, tatu: I don't think this ever gets called. So could... just not implement?
     @Override
     public Date parse(String dateStr, ParsePosition pos)
     {
+        try {
+            return _parseDate(dateStr, pos);
+        } catch (ParseException e) {
+            // may look weird but this is what `DateFormat` suggest to do...
+        }
+        return null;
+    }
+
+    protected Date _parseDate(String dateStr, ParsePosition pos) throws ParseException
+    {
         if (looksLikeISO8601(dateStr)) { // also includes "plain"
-            try {
-                return parseAsISO8601(dateStr, pos, false);
-            } catch (ParseException e) { // will NOT be thrown due to false but is declared...
-                return null;
-            }
+            return parseAsISO8601(dateStr, pos);
         }
         // Also consider "stringified" simple time stamp
         int i = dateStr.length();
@@ -355,13 +402,12 @@
                 }
             }
         }
-        if (i < 0) { // all digits
+        if ((i < 0)
             // let's just assume negative numbers are fine (can't be RFC-1123 anyway); check length for positive
-            if (dateStr.charAt(0) == '-' || NumberInput.inLongRange(dateStr, false)) {
-                return new Date(Long.parseLong(dateStr));
-            }
+                && (dateStr.charAt(0) == '-' || NumberInput.inLongRange(dateStr, false))) {
+            return _parseDateFromLong(dateStr, pos);
         }
-        // Otherwise, fall back to using RFC 1123
+        // Otherwise, fall back to using RFC 1123. NOTE: call will NOT throw, just returns `null`
         return parseAsRFC1123(dateStr, pos);
     }
 
@@ -375,28 +421,114 @@
     public StringBuffer format(Date date, StringBuffer toAppendTo,
             FieldPosition fieldPosition)
     {
-        if (_formatISO8601 == null) {
-            _formatISO8601 = _cloneFormat(DATE_FORMAT_ISO8601, DATE_FORMAT_STR_ISO8601,
-                    _timezone, _locale, _lenient);
+        TimeZone tz = _timezone;
+        if (tz == null) {
+            tz = DEFAULT_TIMEZONE;
         }
-        return _formatISO8601.format(date, toAppendTo, fieldPosition);
+        _format(tz, _locale, date, toAppendTo);
+        return toAppendTo;
+    }
+    
+    protected void _format(TimeZone tz, Locale loc, Date date,
+            StringBuffer buffer)
+    {
+        Calendar cal = _getCalendar(tz);
+        cal.setTime(date);
+
+        pad4(buffer, cal.get(Calendar.YEAR));
+        buffer.append('-');
+        pad2(buffer, cal.get(Calendar.MONTH) + 1);
+        buffer.append('-');
+        pad2(buffer, cal.get(Calendar.DAY_OF_MONTH));
+        buffer.append('T');
+        pad2(buffer, cal.get(Calendar.HOUR_OF_DAY));
+        buffer.append(':');
+        pad2(buffer, cal.get(Calendar.MINUTE));
+        buffer.append(':');
+        pad2(buffer, cal.get(Calendar.SECOND));
+        buffer.append('.');
+        pad3(buffer, cal.get(Calendar.MILLISECOND));
+
+        int offset = tz.getOffset(cal.getTimeInMillis());
+        if (offset != 0) {
+            int hours = Math.abs((offset / (60 * 1000)) / 60);
+            int minutes = Math.abs((offset / (60 * 1000)) % 60);
+            buffer.append(offset < 0 ? '-' : '+');
+            pad2(buffer, hours);
+            if( _tzSerializedWithColon ) {
+            		buffer.append(':');
+            }
+            pad2(buffer, minutes);
+        } else {
+            // 24-Jun-2017, tatu: While `Z` would be conveniently short, older specs
+            //   mandate use of full `+0000`
+//            formatted.append('Z');
+	        	if( _tzSerializedWithColon ) {
+	            buffer.append("+00:00");
+	        	}
+	        	else {
+	        		buffer.append("+0000");
+	        	}
+        }
     }
 
+    private static void pad2(StringBuffer buffer, int value) {
+        int tens = value / 10;
+        if (tens == 0) {
+            buffer.append('0');
+        } else {
+            buffer.append((char) ('0' + tens));
+            value -= 10 * tens;
+        }
+        buffer.append((char) ('0' + value));
+    }
+
+    private static void pad3(StringBuffer buffer, int value) {
+        int h = value / 100;
+        if (h == 0) {
+            buffer.append('0');
+        } else {
+            buffer.append((char) ('0' + h));
+            value -= (h * 100);
+        }
+        pad2(buffer, value);
+    }
+
+    private static void pad4(StringBuffer buffer, int value) {
+        int h = value / 100;
+        if (h == 0) {
+            buffer.append('0').append('0');
+        } else {
+            pad2(buffer, h);
+            value -= (100 * h);
+        }
+        pad2(buffer, value);
+    }
+    
     /*
     /**********************************************************
     /* Std overrides
     /**********************************************************
      */
-    
+
     @Override
     public String toString() {
-        String str = "DateFormat "+getClass().getName();
-        TimeZone tz = _timezone;
-        if (tz != null) {
-            str += " (timezone: "+tz+")";
-        }
-        str += "(locale: "+_locale+")";
-        return str;
+        return String.format("DateFormat %s: (timezone: %s, locale: %s, lenient: %s)",
+                getClass().getName(), _timezone, _locale, _lenient);
+    }
+
+    public String toPattern() { // same as SimpleDateFormat
+        StringBuilder sb = new StringBuilder(100);
+        sb.append("[one of: '")
+            .append(DATE_FORMAT_STR_ISO8601)
+            .append("', '")
+            .append(DATE_FORMAT_STR_RFC1123)
+            .append("' (")
+            ;
+        sb.append(Boolean.FALSE.equals(_lenient) ?
+                "strict" : "lenient")
+            .append(")]");
+        return sb.toString();
     }
 
     @Override // since 2.7[.2], as per [databind#1130]
@@ -411,146 +543,172 @@
 
     /*
     /**********************************************************
-    /* Helper methods
+    /* Helper methods, parsing
     /**********************************************************
      */
 
     /**
-     * Overridable helper method used to figure out which of supported
-     * formats is the likeliest match.
+     * Helper method used to figure out if input looks like valid
+     * ISO-8601 string.
      */
     protected boolean looksLikeISO8601(String dateStr)
     {
-        if (dateStr.length() >= 5
+        if (dateStr.length() >= 7 // really need 10, but...
             && Character.isDigit(dateStr.charAt(0))
             && Character.isDigit(dateStr.charAt(3))
             && dateStr.charAt(4) == '-'
+            && Character.isDigit(dateStr.charAt(5))
             ) {
             return true;
         }
         return false;
     }
 
-    protected Date parseAsISO8601(String dateStr, ParsePosition pos, boolean throwErrors)
-            throws ParseException
+    private Date _parseDateFromLong(String longStr, ParsePosition pos) throws ParseException
     {
-        /* 21-May-2009, tatu: DateFormat has very strict handling of
-         * timezone  modifiers for ISO-8601. So we need to do some scrubbing.
-         */
+        long ts;
+        try {
+            ts = NumberInput.parseLong(longStr);
+        } catch (NumberFormatException e) {
+            throw new ParseException(String.format(
+                    "Timestamp value %s out of 64-bit value range", longStr),
+                    pos.getErrorIndex());
+        }
+        return new Date(ts);
+    }
 
-        /* First: do we have "zulu" format ('Z' == "UTC")? If yes, that's
-         * quite simple because we already set date format timezone to be
-         * UTC, and hence can just strip out 'Z' altogether
-         */
-        int len = dateStr.length();
-        char c = dateStr.charAt(len-1);
-        DateFormat df;
+    protected Date parseAsISO8601(String dateStr, ParsePosition pos)
+        throws ParseException
+    {
+        try {
+            return _parseAsISO8601(dateStr, pos);
+        } catch (IllegalArgumentException e) {
+            throw new ParseException(String.format("Cannot parse date \"%s\", problem: %s",
+                    dateStr, e.getMessage()),
+                    pos.getErrorIndex());
+        }
+    }
+
+    protected Date _parseAsISO8601(String dateStr, ParsePosition bogus)
+        throws IllegalArgumentException, ParseException
+    {
+        final int totalLen = dateStr.length();
+        // actually, one short-cut: if we end with "Z", must be UTC
+        TimeZone tz = DEFAULT_TIMEZONE;
+        if ((_timezone != null) && ('Z' != dateStr.charAt(totalLen-1))) {
+            tz = _timezone;
+        }
+        Calendar cal = _getCalendar(tz);
+        cal.clear();
         String formatStr;
+        if (totalLen <= 10) {
+            Matcher m = PATTERN_PLAIN.matcher(dateStr);
+            if (m.matches()) {
+                int year = _parse4D(dateStr, 0);
+                int month = _parse2D(dateStr, 5)-1;
+                int day = _parse2D(dateStr, 8);
 
-        // Need to support "plain" date...
-        if (len <= 10 && Character.isDigit(c)) {
-            df = _formatPlain;
+                cal.set(year, month, day, 0, 0, 0);
+                cal.set(Calendar.MILLISECOND, 0);
+                return cal.getTime();
+            }
             formatStr = DATE_FORMAT_STR_PLAIN;
-            if (df == null) {
-                df = _formatPlain = _cloneFormat(DATE_FORMAT_PLAIN, formatStr,
-                        _timezone, _locale, _lenient);
-            }
-        } else if (c == 'Z') {
-            df = _formatISO8601_z;
-            formatStr = DATE_FORMAT_STR_ISO8601_Z;
-            if (df == null) {
-                // 10-Jun-2017, tatu: As per [databind#1651], when using this format,
-                //    must use UTC, not whatever is configured as default timezone
-                //    (because we know `Z` identifier is used)
-                df = _formatISO8601_z = _cloneFormat(DATE_FORMAT_ISO8601_Z, formatStr,
-                        DEFAULT_TIMEZONE, _locale, _lenient);
-            }
-            // may be missing milliseconds... if so, add
-            if (dateStr.charAt(len-4) == ':') {
-                StringBuilder sb = new StringBuilder(dateStr);
-                sb.insert(len-1, ".000");
-                dateStr = sb.toString();
-            }
         } else {
-            // Let's see if we have timezone indicator or not...
-            if (hasTimeZone(dateStr)) {
-                c = dateStr.charAt(len-3);
-                if (c == ':') { // remove optional colon
-                    // remove colon
-                    StringBuilder sb = new StringBuilder(dateStr);
-                    sb.delete(len-3, len-2);
-                    dateStr = sb.toString();
-                } else if (c == '+' || c == '-') { // missing minutes
-                    // let's just append '00'
-                    dateStr += "00";
-                }
-                // Milliseconds partial or missing; and even seconds are optional
-                len = dateStr.length();
-                // remove 'T', '+'/'-' and 4-digit timezone-offset
-                int timeLen = len - dateStr.lastIndexOf('T') - 6;
-                if (timeLen < 12) { // 8 for hh:mm:ss, 4 for .sss
-                    int offset = len - 5; // insertion offset, before tz-offset
-                    StringBuilder sb = new StringBuilder(dateStr);
-                    switch (timeLen) {
-                    case 11:
-                        sb.insert(offset, '0'); break;
-                    case 10:
-                        sb.insert(offset, "00"); break;
-                    case 9: // is this legal? (just second fraction marker)
-                        sb.insert(offset, "000"); break;
-                    case 8:
-                        sb.insert(offset, ".000"); break;
-                    case 7: // not legal to have single-digit second
-                        break;
-                    case 6: // probably not legal, but let's allow
-                        sb.insert(offset, "00.000");
-                    case 5: // is legal to omit seconds
-                        sb.insert(offset, ":00.000");
+            Matcher m = PATTERN_ISO8601.matcher(dateStr);
+            if (m.matches()) {
+                // Important! START with optional time zone; otherwise Calendar will explode
+                
+                int start = m.start(2);
+                int end = m.end(2);
+                int len = end-start;
+                if (len > 1) { // 0 -> none, 1 -> 'Z'
+                    // NOTE: first char is sign; then 2 digits, then optional colon, optional 2 digits
+                    int offsetSecs = _parse2D(dateStr, start+1) * 3600; // hours
+                    if (len >= 5) {
+                        offsetSecs += _parse2D(dateStr, end-2) * 60; // minutes
                     }
-                    dateStr = sb.toString();
-                }
-                df = _formatISO8601;
-                formatStr = DATE_FORMAT_STR_ISO8601;
-                if (_formatISO8601 == null) {
-                    df = _formatISO8601 = _cloneFormat(DATE_FORMAT_ISO8601, formatStr,
-                            _timezone, _locale, _lenient);
-                }
-            } else {
-                // If not, plain date, no timezone
-                int timeLen = len - dateStr.lastIndexOf('T') - 1;
-                // And possible also millisecond part if missing
-                if (timeLen < 12) { // missing, or partial
-                    StringBuilder sb = new StringBuilder(dateStr);
-                    switch (timeLen) {
-                    case 11: sb.append('0');
-                    case 10: sb.append('0');
-                    case 9: sb.append('0');
-                        break;
-                    default:
-                        sb.append(".000");
+                    if (dateStr.charAt(start) == '-') {
+                        offsetSecs *= -1000;
+                    } else {
+                        offsetSecs *= 1000;
                     }
-                    dateStr = sb.toString();
+                    cal.set(Calendar.ZONE_OFFSET, offsetSecs);
+                    // 23-Jun-2017, tatu: Not sure why, but this appears to be needed too:
+                    cal.set(Calendar.DST_OFFSET, 0);
                 }
-                df = _formatISO8601_noTz;
-                formatStr = DATE_FORMAT_STR_ISO8601_NO_TZ;
-                if (df == null) {
-                    // 10-Jun-2017, tatu: As per [databind#1651], when using this format,
-                    //    must use UTC, not whatever is configured as default timezone
-                    //    (because we know `Z` identifier is used)
-                    df = _formatISO8601_noTz = _cloneFormat(DATE_FORMAT_ISO8601_NO_TZ, formatStr,
-                            _timezone, _locale, _lenient);
+                
+                int year = _parse4D(dateStr, 0);
+                int month = _parse2D(dateStr, 5)-1;
+                int day = _parse2D(dateStr, 8);
+
+                // So: 10 chars for date, then `T`, so starts at 11
+                int hour = _parse2D(dateStr, 11);
+                int minute = _parse2D(dateStr, 14);
+
+                // Seconds are actually optional... so
+                int seconds;
+                if ((totalLen > 16) && dateStr.charAt(16) == ':') {
+                    seconds = _parse2D(dateStr, 17);
+                } else {
+                    seconds = 0;
                 }
+                cal.set(year, month, day, hour, minute, seconds);
+
+                // Optional milliseconds
+                start = m.start(1) + 1;
+                end = m.end(1);
+                int msecs = 0;
+                if (start >= end) { // no fractional
+                    cal.set(Calendar.MILLISECOND, 0);
+                } else {
+                    // first char is '.', but rest....
+                    msecs = 0;
+                    final int fractLen = end-start;
+                    switch (fractLen) {
+                    default: // [databind#1745] Allow longer fractions... for now, cap at nanoseconds tho
+
+                        if (fractLen > 9) { // only allow up to nanos
+                            throw new ParseException(String.format(
+"Cannot parse date \"%s\": invalid fractional seconds '%s'; can use at most 9 digits",
+                                       dateStr, m.group(1).substring(1)
+                                       ), start);
+                        }
+                        // fall through
+                    case 3:
+                        msecs += (dateStr.charAt(start+2) - '0');
+                    case 2:
+                        msecs += 10 * (dateStr.charAt(start+1) - '0');
+                    case 1:
+                        msecs += 100 * (dateStr.charAt(start) - '0');
+                        break;
+                    case 0:
+                        break;
+                    }
+                    cal.set(Calendar.MILLISECOND, msecs);
+                }
+                return cal.getTime();
             }
+            formatStr = DATE_FORMAT_STR_ISO8601;
         }
-        Date dt = df.parse(dateStr, pos);
-        // 22-Dec-2015, tatu: With non-lenient, may get null
-        if (dt == null) {
-            throw new ParseException
-            (String.format("Can not parse date \"%s\": while it seems to fit format '%s', parsing fails (leniency? %s)",
-                           dateStr, formatStr, _lenient),
-               pos.getErrorIndex());
-        }
-        return dt;
+
+        throw new ParseException
+        (String.format("Cannot parse date \"%s\": while it seems to fit format '%s', parsing fails (leniency? %s)",
+                       dateStr, formatStr, _lenient),
+                // [databind#1742]: Might be able to give actual location, some day, but for now
+                //  we can't give anything more indicative
+                0);
+    }
+
+    private static int _parse4D(String str, int index) {
+        return (1000 * (str.charAt(index) - '0'))
+                + (100 * (str.charAt(index+1) - '0'))
+                + (10 * (str.charAt(index+2) - '0'))
+                + (str.charAt(index+3) - '0');
+    }
+
+    private static int _parse2D(String str, int index) {
+        return (10 * (str.charAt(index) - '0'))
+                + (str.charAt(index+1) - '0');
     }
 
     protected Date parseAsRFC1123(String dateStr, ParsePosition pos)
@@ -562,20 +720,11 @@
         return _formatRFC1123.parse(dateStr, pos);
     }
 
-    private final static boolean hasTimeZone(String str)
-    {
-        // Only accept "+hh", "+hhmm" and "+hh:mm" (and with minus), so
-        int len = str.length();
-        if (len >= 6) {
-            char c = str.charAt(len-6);
-            if (c == '+' || c == '-') return true;
-            c = str.charAt(len-5);
-            if (c == '+' || c == '-') return true;
-            c = str.charAt(len-3);
-            if (c == '+' || c == '-') return true;
-        }
-        return false;
-    }
+    /*
+    /**********************************************************
+    /* Helper methods, other
+    /**********************************************************
+     */
 
     private final static DateFormat _cloneFormat(DateFormat df, String format,
             TimeZone tz, Locale loc, Boolean lenient)
@@ -597,11 +746,24 @@
 
     protected void _clearFormats() {
         _formatRFC1123 = null;
-        _formatISO8601 = null;
-        _formatISO8601_z = null;
-        _formatISO8601_noTz = null;
+    }
 
-        _formatPlain = null;
+    protected Calendar _getCalendar(TimeZone tz) {
+        Calendar cal = _calendar;
+        if (cal == null ) {
+            _calendar = cal = (Calendar)CALENDAR.clone();
+        }
+        if (!cal.getTimeZone().equals(tz) ) {
+            cal.setTimeZone(tz);
+        }
+        cal.setLenient(isLenient());
+        return cal;
+    }
+    
+    protected static <T> boolean _equals(T value1, T value2) {
+        if (value1 == value2) {
+            return true;
+        }
+        return (value1 != null) && value1.equals(value2);
     }
 }
-
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/TokenBuffer.java b/src/main/java/com/fasterxml/jackson/databind/util/TokenBuffer.java
index 1f05c32..f31334e 100644
--- a/src/main/java/com/fasterxml/jackson/databind/util/TokenBuffer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/util/TokenBuffer.java
@@ -7,7 +7,6 @@
 
 import com.fasterxml.jackson.core.*;
 import com.fasterxml.jackson.core.base.ParserMinimalBase;
-import com.fasterxml.jackson.core.json.JsonReadContext;
 import com.fasterxml.jackson.core.json.JsonWriteContext;
 import com.fasterxml.jackson.core.util.ByteArrayBuilder;
 import com.fasterxml.jackson.databind.*;
@@ -41,11 +40,19 @@
     /**
      * Object codec to use for stream-based object
      * conversion through parser/generator interfaces. If null,
-     * such methods can not be used.
+     * such methods cannot be used.
      */
     protected ObjectCodec _objectCodec;
 
     /**
+     * Parse context from "parent" parser (one from which content to buffer is read,
+     * if specified). Used, if available, when reading content, to present full
+     * context as if content was read from the original parser: this is useful
+     * in error reporting and sometimes processing as well.
+     */
+    protected JsonStreamContext _parentContext;
+
+    /**
      * Bit flag composed of bits that indicate which
      * {@link com.fasterxml.jackson.core.JsonGenerator.Feature}s
      * are enabled.
@@ -135,19 +142,7 @@
     /**
      * @param codec Object codec to use for stream-based object
      *   conversion through parser/generator interfaces. If null,
-     *   such methods can not be used.
-     *   
-     * @deprecated since 2.3 preferred variant is one that takes {@link JsonParser} or additional boolean parameter.
-     */
-    @Deprecated
-    public TokenBuffer(ObjectCodec codec) {
-        this(codec, false);
-    }
-
-    /**
-     * @param codec Object codec to use for stream-based object
-     *   conversion through parser/generator interfaces. If null,
-     *   such methods can not be used.
+     *   such methods cannot be used.
      * @param hasNativeIds Whether resulting {@link JsonParser} (if created)
      *   is considered to support native type and object ids
      */
@@ -178,6 +173,7 @@
     public TokenBuffer(JsonParser p, DeserializationContext ctxt)
     {
         _objectCodec = p.getCodec();
+        _parentContext = p.getParsingContext();
         _generatorFeatures = DEFAULT_GENERATOR_FEATURES;
         _writeContext = JsonWriteContext.createRootContext(null);
         // at first we have just one segment
@@ -191,6 +187,35 @@
     }
 
     /**
+     * Convenience method, equivalent to:
+     *<pre>
+     * TokenBuffer b = new TokenBuffer(p);
+     * b.copyCurrentStructure(p);
+     * return b;
+     *</pre>
+     *
+     * @since 2.9
+     */
+    public static TokenBuffer asCopyOfValue(JsonParser p) throws IOException {
+        TokenBuffer b = new TokenBuffer(p);
+        b.copyCurrentStructure(p);
+        return b;
+    }
+
+    /**
+     * Method that allows explicitly specifying parent parse context to associate
+     * with contents of this buffer. Usually context is assigned at construction,
+     * based on given parser; but it is not always available, and may not contain
+     * intended context.
+     *
+     * @since 2.9
+     */
+    public TokenBuffer overrideParentContext(JsonStreamContext ctxt) {
+        _parentContext = ctxt;
+        return this;
+    }
+
+    /**
      * @since 2.7
      */
     public TokenBuffer forceUseOfBigDecimal(boolean b) {
@@ -213,12 +238,27 @@
      * 
      * @return Parser that can be used for reading contents stored in this buffer
      */
-    public JsonParser asParser()
-    {
+    public JsonParser asParser() {
         return asParser(_objectCodec);
     }
 
     /**
+     * Same as:
+     *<pre>
+     *  JsonParser p = asParser();
+     *  p.nextToken();
+     *  return p;
+     *</pre>
+     *
+     * @since 2.9
+     */
+    public JsonParser asParserOnFirstToken() throws IOException {
+        JsonParser p = asParser(_objectCodec);
+        p.nextToken();
+        return p;
+    }
+
+    /**
      * Method used to create a {@link JsonParser} that can read contents
      * stored in this buffer.
      *<p>
@@ -227,13 +267,13 @@
      *
      * @param codec Object codec to use for stream-based object
      *   conversion through parser/generator interfaces. If null,
-     *   such methods can not be used.
+     *   such methods cannot be used.
      * 
      * @return Parser that can be used for reading contents stored in this buffer
      */
     public JsonParser asParser(ObjectCodec codec)
     {
-        return new Parser(_first, codec, _hasNativeTypeIds, _hasNativeObjectIds);
+        return new Parser(_first, codec, _hasNativeTypeIds, _hasNativeObjectIds, _parentContext);
     }
 
     /**
@@ -242,11 +282,11 @@
      */
     public JsonParser asParser(JsonParser src)
     {
-        Parser p = new Parser(_first, src.getCodec(), _hasNativeTypeIds, _hasNativeObjectIds);
+        Parser p = new Parser(_first, src.getCodec(), _hasNativeTypeIds, _hasNativeObjectIds, _parentContext);
         p.setLocation(src.getTokenLocation());
         return p;
     }
-    
+
     /*
     /**********************************************************
     /* Additional accessors
@@ -254,12 +294,10 @@
      */
 
     public JsonToken firstToken() {
-        if (_first != null) {
-            return _first.type(0);
-        }
-        return null;
+        // no need to null check; never create without `_first`
+        return _first.type(0);
     }
-    
+
     /*
     /**********************************************************
     /* Other custom methods not needed for implementing interfaces
@@ -291,16 +329,16 @@
         }
         return this;
     }
-    
+
     /**
      * Helper method that will write all contents of this buffer
      * using given {@link JsonGenerator}.
      *<p>
      * Note: this method would be enough to implement
      * <code>JsonSerializer</code>  for <code>TokenBuffer</code> type;
-     * but we can not have upwards
+     * but we cannot have upwards
      * references (from core to mapper package); and as such we also
-     * can not take second argument.
+     * cannot take second argument.
      */
     public void serialize(JsonGenerator gen) throws IOException
     {
@@ -397,7 +435,7 @@
                         gen.writeNumber((String) n);
                     } else {
                         throw new JsonGenerationException(String.format(
-                                "Unrecognized value type for VALUE_NUMBER_FLOAT: %s, can not serialize",
+                                "Unrecognized value type for VALUE_NUMBER_FLOAT: %s, cannot serialize",
                                 n.getClass().getName()), gen);
                     }
                 }
@@ -453,7 +491,7 @@
             copyCurrentStructure(p);
         } while ((t = p.nextToken()) == JsonToken.FIELD_NAME);
         if (t != JsonToken.END_OBJECT) {
-            ctxt.reportWrongTokenException(p, JsonToken.END_OBJECT,
+            ctxt.reportWrongTokenException(TokenBuffer.class, JsonToken.END_OBJECT,
                     "Expected END_OBJECT after copying contents of a JsonParser into TokenBuffer, got "+t);
             // never gets here
         }
@@ -909,7 +947,7 @@
 
     /**
      * Although we could support this method, it does not necessarily make
-     * sense: we can not make good use of streaming because buffer must
+     * sense: we cannot make good use of streaming because buffer must
      * hold all the data. Because of this, currently this will simply
      * throw {@link UnsupportedOperationException}
      */
@@ -1157,7 +1195,9 @@
             _appendAt = 1;
         }
     }
-    
+
+    // 21-Oct-2016, tatu: Does not seem to be used or needed
+    /*
     protected final void _appendRaw(int rawType, Object value)
     {
         Segment next = _hasNativeId
@@ -1170,6 +1210,7 @@
             _appendAt = 1;
         }
     }
+    */
 
     @Override
     protected void _reportUnsupportedOperation() {
@@ -1225,7 +1266,7 @@
          * Information about parser context, context in which
          * the next token is to be parsed (root, array, object).
          */
-        protected JsonReadContext _parsingContext;
+        protected TokenBufferReadContext _parsingContext;
         
         protected boolean _closed;
 
@@ -1239,15 +1280,22 @@
         /**********************************************************
          */
 
+        @Deprecated // since 2.9
         public Parser(Segment firstSeg, ObjectCodec codec,
-                boolean hasNativeTypeIds,
-                boolean hasNativeObjectIds)
+                boolean hasNativeTypeIds, boolean hasNativeObjectIds)
+        {
+            this(firstSeg, codec, hasNativeTypeIds, hasNativeObjectIds, null);
+        }
+        
+        public Parser(Segment firstSeg, ObjectCodec codec,
+                boolean hasNativeTypeIds, boolean hasNativeObjectIds,
+                JsonStreamContext parentContext)
         {
             super(0);
             _segment = firstSeg;
             _segmentPtr = -1; // not yet read
             _codec = codec;
-            _parsingContext = JsonReadContext.createRootContext(null);
+            _parsingContext = TokenBufferReadContext.createRootContext(parentContext);
             _hasNativeTypeIds = hasNativeTypeIds;
             _hasNativeObjectIds = hasNativeObjectIds;
             _hasNativeIds = (hasNativeTypeIds | hasNativeObjectIds);
@@ -1327,17 +1375,13 @@
                 String name = (ob instanceof String) ? ((String) ob) : ob.toString();
                 _parsingContext.setCurrentName(name);
             } else if (_currToken == JsonToken.START_OBJECT) {
-                _parsingContext = _parsingContext.createChildObjectContext(-1, -1);
+                _parsingContext = _parsingContext.createChildObjectContext();
             } else if (_currToken == JsonToken.START_ARRAY) {
-                _parsingContext = _parsingContext.createChildArrayContext(-1, -1);
+                _parsingContext = _parsingContext.createChildArrayContext();
             } else if (_currToken == JsonToken.END_OBJECT
                     || _currToken == JsonToken.END_ARRAY) {
                 // Closing JSON Object/Array? Close matching context
-                _parsingContext = _parsingContext.getParent();
-                // but allow unbalanced cases too (more close markers)
-                if (_parsingContext == null) {
-                    _parsingContext = JsonReadContext.createRootContext(null);
-                }
+                _parsingContext = _parsingContext.parentOrCopy();
             }
             return _currToken;
         }
@@ -1346,11 +1390,14 @@
         public String nextFieldName() throws IOException
         {
             // inlined common case from nextToken()
-            if (_closed || (_segment == null)) return null;
+            if (_closed || (_segment == null)) {
+                return null;
+            }
 
             int ptr = _segmentPtr+1;
-            if (ptr < Segment.TOKENS_PER_SEGMENT && _segment.type(ptr) == JsonToken.FIELD_NAME) {
+            if ((ptr < Segment.TOKENS_PER_SEGMENT) && (_segment.type(ptr) == JsonToken.FIELD_NAME)) {
                 _segmentPtr = ptr;
+                _currToken = JsonToken.FIELD_NAME;
                 Object ob = _segment.get(ptr); // inlined _currentObject();
                 String name = (ob instanceof String) ? ((String) ob) : ob.toString();
                 _parsingContext.setCurrentName(name);
@@ -1383,7 +1430,7 @@
         public String getCurrentName() {
             // 25-Jun-2015, tatu: as per [databind#838], needs to be same as ParserBase
             if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) {
-                JsonReadContext parent = _parsingContext.getParent();
+                JsonStreamContext parent = _parsingContext.getParent();
                 return parent.getCurrentName();
             }
             return _parsingContext.getCurrentName();
@@ -1393,14 +1440,16 @@
         public void overrideCurrentName(String name)
         {
             // Simple, but need to look for START_OBJECT/ARRAY's "off-by-one" thing:
-            JsonReadContext ctxt = _parsingContext;
+            JsonStreamContext ctxt = _parsingContext;
             if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) {
                 ctxt = ctxt.getParent();
             }
-            try {
-                ctxt.setCurrentName(name);
-            } catch (IOException e) {
-                throw new RuntimeException(e);
+            if (ctxt instanceof TokenBufferReadContext) {
+                try {
+                    ((TokenBufferReadContext) ctxt).setCurrentName(name);
+                } catch (IOException e) {
+                    throw new RuntimeException(e);
+                }
             }
         }
 
@@ -1420,7 +1469,7 @@
                 if (ob instanceof String) {
                     return (String) ob;
                 }
-                return (ob == null) ? null : ob.toString();
+                return ClassUtil.nullOrToString(ob);
             }
             if (_currToken == null) {
                 return null;
@@ -1428,8 +1477,7 @@
             switch (_currToken) {
             case VALUE_NUMBER_INT:
             case VALUE_NUMBER_FLOAT:
-                Object ob = _currentObject();
-                return (ob == null) ? null : ob.toString();
+                return ClassUtil.nullOrToString(_currentObject());
             default:
             	return _currToken.asString();
             }
@@ -1455,7 +1503,7 @@
             // We never have raw buffer available, so:
             return false;
         }
-        
+
         /*
         /**********************************************************
         /* Public API, access to token information, numeric
@@ -1463,6 +1511,23 @@
          */
 
         @Override
+        public boolean isNaN() {
+            // can only occur for floating-point numbers
+            if (_currToken == JsonToken.VALUE_NUMBER_FLOAT) {
+                Object value = _currentObject();
+                if (value instanceof Double) {
+                    Double v = (Double) value;
+                    return v.isNaN() || v.isInfinite();
+                }
+                if (value instanceof Float) {
+                    Float v = (Float) value;
+                    return v.isNaN() || v.isInfinite();
+                }
+            }
+            return false;
+        }
+
+        @Override
         public BigInteger getBigIntegerValue() throws IOException
         {
             Number n = getNumberValue();
@@ -1508,16 +1573,22 @@
         @Override
         public int getIntValue() throws IOException
         {
-            // optimize common case:
-            if (_currToken == JsonToken.VALUE_NUMBER_INT) {
-                return ((Number) _currentObject()).intValue();
+            Number n = (_currToken == JsonToken.VALUE_NUMBER_INT) ?
+                    ((Number) _currentObject()) : getNumberValue();
+            if ((n instanceof Integer) || _smallerThanInt(n)) {
+                return n.intValue();
             }
-            return getNumberValue().intValue();
+            return _convertNumberToInt(n);
         }
 
         @Override
         public long getLongValue() throws IOException {
-            return getNumberValue().longValue();
+            Number n = (_currToken == JsonToken.VALUE_NUMBER_INT) ?
+                    ((Number) _currentObject()) : getNumberValue();
+            if ((n instanceof Long) || _smallerThanLong(n)) {
+                return n.longValue();
+            }
+            return _convertNumberToLong(n);
         }
 
         @Override
@@ -1542,7 +1613,7 @@
                 return (Number) value;
             }
             // Difficult to really support numbers-as-Strings; but let's try.
-            // NOTE: no access to DeserializationConfig, unfortunately, so can not
+            // NOTE: no access to DeserializationConfig, unfortunately, so cannot
             // try to determine Double/BigDecimal preference...
             if (value instanceof String) {
                 String str = (String) value;
@@ -1558,6 +1629,78 @@
                     +value.getClass().getName());
         }
 
+        private final boolean _smallerThanInt(Number n) {
+            return (n instanceof Short) || (n instanceof Byte);
+        }
+
+        private final boolean _smallerThanLong(Number n) {
+            return (n instanceof Integer) || (n instanceof Short) || (n instanceof Byte);
+        }
+
+        // 02-Jan-2017, tatu: Modified from method(s) in `ParserBase`
+        
+        protected int _convertNumberToInt(Number n) throws IOException
+        {
+            if (n instanceof Long) {
+                long l = n.longValue();
+                int result = (int) l;
+                if (((long) result) != l) {
+                    reportOverflowInt();
+                }
+                return result;
+            }
+            if (n instanceof BigInteger) {
+                BigInteger big = (BigInteger) n;
+                if (BI_MIN_INT.compareTo(big) > 0 
+                        || BI_MAX_INT.compareTo(big) < 0) {
+                    reportOverflowInt();
+                }
+            } else if ((n instanceof Double) || (n instanceof Float)) {
+                double d = n.doubleValue();
+                // Need to check boundaries
+                if (d < MIN_INT_D || d > MAX_INT_D) {
+                    reportOverflowInt();
+                }
+                return (int) d;
+            } else if (n instanceof BigDecimal) {
+                BigDecimal big = (BigDecimal) n;
+                if (BD_MIN_INT.compareTo(big) > 0 
+                    || BD_MAX_INT.compareTo(big) < 0) {
+                    reportOverflowInt();
+                }
+            } else {
+                _throwInternal();
+            }
+            return n.intValue();
+        }
+
+        protected long _convertNumberToLong(Number n) throws IOException
+        {
+            if (n instanceof BigInteger) {
+                BigInteger big = (BigInteger) n;
+                if (BI_MIN_LONG.compareTo(big) > 0 
+                        || BI_MAX_LONG.compareTo(big) < 0) {
+                    reportOverflowLong();
+                }
+            } else if ((n instanceof Double) || (n instanceof Float)) {
+                double d = n.doubleValue();
+                // Need to check boundaries
+                if (d < MIN_LONG_D || d > MAX_LONG_D) {
+                    reportOverflowLong();
+                }
+                return (long) d;
+            } else if (n instanceof BigDecimal) {
+                BigDecimal big = (BigDecimal) n;
+                if (BD_MIN_LONG.compareTo(big) > 0 
+                    || BD_MAX_LONG.compareTo(big) < 0) {
+                    reportOverflowLong();
+                }
+            } else {
+                _throwInternal();
+            }
+            return n.longValue();
+        }
+
         /*
         /**********************************************************
         /* Public API, access to token information, other
@@ -1587,7 +1730,7 @@
                 // fall through to error case
             }
             if (_currToken != JsonToken.VALUE_STRING) {
-                throw _constructError("Current token ("+_currToken+") not VALUE_STRING (or VALUE_EMBEDDED_OBJECT with byte[]), can not access as binary");
+                throw _constructError("Current token ("+_currToken+") not VALUE_STRING (or VALUE_EMBEDDED_OBJECT with byte[]), cannot access as binary");
             }
             final String str = getText();
             if (str == null) {
@@ -1653,7 +1796,7 @@
         protected final void _checkIsNumber() throws JsonParseException
         {
             if (_currToken == null || !_currToken.isNumeric()) {
-                throw _constructError("Current token ("+_currToken+") not numeric, can not use numeric value accessors");
+                throw _constructError("Current token ("+_currToken+") not numeric, cannot use numeric value accessors");
             }
         }
 
@@ -1795,6 +1938,7 @@
             return _next;
         }
 
+        /*
         public Segment appendRaw(int index, int rawTokenType, Object value)
         {
             if (index < TOKENS_PER_SEGMENT) {
@@ -1818,6 +1962,28 @@
             return _next;
         }
 
+        private void set(int index, int rawTokenType, Object value, Object objectId, Object typeId)
+        {
+            _tokens[index] = value;
+            long typeCode = (long) rawTokenType;
+            if (index > 0) {
+                typeCode <<= (index << 2);
+            }
+            _tokenTypes |= typeCode;
+            assignNativeIds(index, objectId, typeId);
+        }
+
+        private void set(int index, int rawTokenType, Object value)
+        {
+            _tokens[index] = value;
+            long typeCode = (long) rawTokenType;
+            if (index > 0) {
+                typeCode <<= (index << 2);
+            }
+            _tokenTypes |= typeCode;
+        }
+        */
+
         private void set(int index, JsonToken tokenType)
         {
             /* Assumption here is that there are no overwrites, just appends;
@@ -1863,27 +2029,6 @@
             assignNativeIds(index, objectId, typeId);
         }
 
-        private void set(int index, int rawTokenType, Object value)
-        {
-            _tokens[index] = value;
-            long typeCode = (long) rawTokenType;
-            if (index > 0) {
-                typeCode <<= (index << 2);
-            }
-            _tokenTypes |= typeCode;
-        }
-
-        private void set(int index, int rawTokenType, Object value, Object objectId, Object typeId)
-        {
-            _tokens[index] = value;
-            long typeCode = (long) rawTokenType;
-            if (index > 0) {
-                typeCode <<= (index << 2);
-            }
-            _tokenTypes |= typeCode;
-            assignNativeIds(index, objectId, typeId);
-        }
-
         private final void assignNativeIds(int index, Object objectId, Object typeId)
         {
             if (_nativeIds == null) {
@@ -1900,14 +2045,14 @@
         /**
          * @since 2.3
          */
-        public Object findObjectId(int index) {
+        private Object findObjectId(int index) {
             return (_nativeIds == null) ? null : _nativeIds.get(_objectIdIndex(index));
         }
         
         /**
          * @since 2.3
          */
-        public Object findTypeId(int index) {
+        private Object findTypeId(int index) {
             return (_nativeIds == null) ? null : _nativeIds.get(_typeIdIndex(index));
         }
 
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/TokenBufferReadContext.java b/src/main/java/com/fasterxml/jackson/databind/util/TokenBufferReadContext.java
new file mode 100644
index 0000000..6652036
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/util/TokenBufferReadContext.java
@@ -0,0 +1,135 @@
+package com.fasterxml.jackson.databind.util;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.json.JsonReadContext;
+
+/**
+ * Implementation of {@link JsonStreamContext} used by {@link TokenBuffer}
+ * to link back to the original context to try to keep location information
+ * consistent between source location and buffered content when it's re-read
+ * from the buffer.
+ *
+ * @since 2.9
+ */
+public class TokenBufferReadContext extends JsonStreamContext
+{
+    protected final JsonStreamContext _parent;
+
+    protected final JsonLocation _startLocation;
+
+    // Benefit for reusing?
+//    protected JsonReadContext _child;
+
+    /*
+    /**********************************************************
+    /* Location/state information (minus source reference)
+    /**********************************************************
+     */
+
+    protected String _currentName;
+
+    protected Object _currentValue;
+
+    protected TokenBufferReadContext(JsonStreamContext base, Object srcRef) {
+        super(base);
+        _parent = base.getParent();
+        _currentName = base.getCurrentName();
+        _currentValue = base.getCurrentValue();
+        if (base instanceof JsonReadContext) {
+            JsonReadContext rc = (JsonReadContext) base;
+            _startLocation = rc.getStartLocation(srcRef);
+        } else {
+            _startLocation = JsonLocation.NA;
+        }
+    }
+
+    protected TokenBufferReadContext(JsonStreamContext base, JsonLocation startLoc) {
+        super(base);
+        _parent = base.getParent();
+        _currentName = base.getCurrentName();
+        _currentValue = base.getCurrentValue();
+        _startLocation = startLoc;
+    }
+
+    /**
+     * Constructor for case where there is no real surrounding context: just create
+     * virtual ROOT
+     */
+    protected TokenBufferReadContext() {
+        super(TYPE_ROOT, -1);
+        _parent = null;
+        _startLocation = JsonLocation.NA;
+    }
+
+    protected TokenBufferReadContext(TokenBufferReadContext parent, int type, int index) {
+        super(type, index);
+        _parent = parent;
+        _startLocation = parent._startLocation;
+    }
+
+    @Override
+    public Object getCurrentValue() {
+        return _currentValue;
+    }
+
+    @Override
+    public void setCurrentValue(Object v) {
+        _currentValue = v;
+    }
+
+    /*
+    /**********************************************************
+    /* Factory methods
+    /**********************************************************
+     */
+
+    public static TokenBufferReadContext createRootContext(JsonStreamContext origContext) {
+        // First: possible to have no current context; if so, just create bogus ROOT context
+        if (origContext == null) {
+            return new TokenBufferReadContext();
+        }
+        return new TokenBufferReadContext(origContext, null);
+    }
+
+    public TokenBufferReadContext createChildArrayContext() {
+        return new TokenBufferReadContext(this, TYPE_ARRAY, -1);
+    }
+
+    public TokenBufferReadContext createChildObjectContext() {
+        return new TokenBufferReadContext(this, TYPE_OBJECT, -1);
+    }
+
+    /**
+     * Helper method we need to handle discontinuity between "real" contexts buffer
+     * creates, and ones from parent: problem being they are of different types.
+     */
+    public TokenBufferReadContext parentOrCopy() {
+        // 30-Apr-2017, tatu: This is bit awkward since part on ancestor stack is of different
+        //     type (usually `JsonReadContext`)... and so for unbalanced buffers (with extra
+        //     END_OBJECT / END_ARRAY), we may need to create 
+        if (_parent instanceof TokenBufferReadContext) {
+            return (TokenBufferReadContext) _parent;
+        }
+        if (_parent == null) { // unlikely, but just in case let's support
+            return new TokenBufferReadContext();
+        }
+        return new TokenBufferReadContext(_parent, _startLocation);
+    }
+
+    /*
+    /**********************************************************
+    /* Abstract method implementation
+    /**********************************************************
+     */
+
+    @Override public String getCurrentName() { return _currentName; }
+
+    // @since 2.9
+    @Override public boolean hasCurrentName() { return _currentName != null; }
+
+    @Override public JsonStreamContext getParent() { return _parent; }
+
+    public void setCurrentName(String name) throws JsonProcessingException {
+        _currentName = name;
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/BaseMapTest.java b/src/test/java/com/fasterxml/jackson/databind/BaseMapTest.java
index 311ffad..4771d79 100644
--- a/src/test/java/com/fasterxml/jackson/databind/BaseMapTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/BaseMapTest.java
@@ -6,11 +6,9 @@
 import static org.junit.Assert.*;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonValue;
-import com.fasterxml.jackson.core.FormatSchema;
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonProcessingException;
+
+import com.fasterxml.jackson.core.*;
+
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer;
 import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer;
@@ -41,10 +39,8 @@
     protected static class BooleanWrapper {
         public Boolean b;
 
-        @JsonCreator
+        public BooleanWrapper() { }
         public BooleanWrapper(Boolean value) { b = value; }
-
-        @JsonValue public Boolean value() { return b; }
     }
 
     protected static class IntWrapper {
@@ -109,9 +105,14 @@
     {
         public Map<K,V> map;
 
+        public MapWrapper() { }
         public MapWrapper(Map<K,V> m) {
             map = m;
         }
+        public MapWrapper(K key, V value) {
+            map = new LinkedHashMap<>();
+            map.put(key, value);
+        }
     }
     
     protected static class ArrayWrapper<T>
@@ -209,11 +210,11 @@
 
     protected ObjectMapper objectMapper() {
         if (SHARED_MAPPER == null) {
-            SHARED_MAPPER = new ObjectMapper();
+            SHARED_MAPPER = newObjectMapper();
         }
         return SHARED_MAPPER;
     }
-    
+
     protected ObjectWriter objectWriter() {
         return objectMapper().writer();
     }
@@ -226,6 +227,11 @@
         return objectMapper().readerFor(cls);
     }
 
+    // @since 2.9
+    protected static ObjectMapper newObjectMapper() {
+        return new ObjectMapper();
+    }
+
     // @since 2.7
     protected TypeFactory newTypeFactory() {
         // this is a work-around; no null modifier added
@@ -243,6 +249,11 @@
         assertArrayEquals(exp, act);
     }
 
+    protected void assertEquals(byte[] exp, byte[] act)
+    {
+        assertArrayEquals(exp, act);
+    }
+
     /**
      * Helper method for verifying 3 basic cookie cutter cases;
      * identity comparison (true), and against null (false),
diff --git a/src/test/java/com/fasterxml/jackson/databind/BaseTest.java b/src/test/java/com/fasterxml/jackson/databind/BaseTest.java
index ef03fda..e4d664b 100644
--- a/src/test/java/com/fasterxml/jackson/databind/BaseTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/BaseTest.java
@@ -59,28 +59,28 @@
 
         public static class Name
         {
-          private String _first, _last;
+            private String _first, _last;
 
-          public Name() { }
-          public Name(String f, String l) {
-              _first = f;
-              _last = l;
-          }
-          
-          public String getFirst() { return _first; }
-          public String getLast() { return _last; }
+            public Name() { }
+            public Name(String f, String l) {
+                _first = f;
+                _last = l;
+            }
 
-          public void setFirst(String s) { _first = s; }
-          public void setLast(String s) { _last = s; }
+            public String getFirst() { return _first; }
+            public String getLast() { return _last; }
 
-          @Override
-          public boolean equals(Object o)
-          {
-              if (o == this) return true;
-              if (o == null || o.getClass() != getClass()) return false;
-              Name other = (Name) o;
-              return _first.equals(other._first) && _last.equals(other._last); 
-          }
+            public void setFirst(String s) { _first = s; }
+            public void setLast(String s) { _last = s; }
+
+            @Override
+            public boolean equals(Object o)
+            {
+                if (o == this) return true;
+                if (o == null || o.getClass() != getClass()) return false;
+                Name other = (Name) o;
+                return _first.equals(other._first) && _last.equals(other._last); 
+            }
         }
 
         private Gender _gender;
@@ -267,16 +267,6 @@
         assertEquals(String.valueOf(expValue), jp.getText());
     }
 
-    /**
-     * Method that checks whether Unit tests appear to run from Ant build
-     * scripts.
-     * 
-     * @since 1.6
-     */
-    protected static boolean runsFromAnt() {
-        return "true".equals(System.getProperty("FROM_ANT"));
-    }
-    
     /*
     /**********************************************************
     /* Parser/generator construction
diff --git a/src/test/java/com/fasterxml/jackson/databind/FullStreamReadTest.java b/src/test/java/com/fasterxml/jackson/databind/FullStreamReadTest.java
new file mode 100644
index 0000000..6bfa5cc
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/FullStreamReadTest.java
@@ -0,0 +1,175 @@
+package com.fasterxml.jackson.databind;
+
+import java.util.*;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.exc.MismatchedInputException;
+
+public class FullStreamReadTest extends BaseMapTest
+{
+    private final static String JSON_OK_ARRAY = " [ 1, 2, 3]    ";
+    private final static String JSON_OK_ARRAY_WITH_COMMENT = JSON_OK_ARRAY + " // stuff ";
+
+    private final static String JSON_FAIL_ARRAY = JSON_OK_ARRAY + " [ ]";
+
+    /*
+    /**********************************************************
+    /* Test methods, config
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = newObjectMapper();
+
+    public void testMapperAcceptTrailing() throws Exception
+    {
+        assertFalse(MAPPER.isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS));
+
+        // by default, should be ok to read, all
+        _verifyArray(MAPPER.readTree(JSON_OK_ARRAY));
+        _verifyArray(MAPPER.readTree(JSON_OK_ARRAY_WITH_COMMENT));
+        _verifyArray(MAPPER.readTree(JSON_FAIL_ARRAY));
+
+        // and also via "untyped"
+        _verifyCollection(MAPPER.readValue(JSON_OK_ARRAY, List.class));
+        _verifyCollection(MAPPER.readValue(JSON_OK_ARRAY_WITH_COMMENT, List.class));
+        _verifyCollection(MAPPER.readValue(JSON_FAIL_ARRAY, List.class));
+    }
+
+    public void testMapperFailOnTrailing() throws Exception
+    {
+        // but things change if we enforce checks
+        ObjectMapper strict = newObjectMapper()
+                .enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS);
+        assertTrue(strict.isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS));
+
+        // some still ok
+        _verifyArray(strict.readTree(JSON_OK_ARRAY));
+        _verifyCollection(strict.readValue(JSON_OK_ARRAY, List.class));
+
+        // but if real content exists, will fail
+        try {
+            strict.readTree(JSON_FAIL_ARRAY);
+            fail("Should not have passed");
+        } catch (MismatchedInputException e) {
+            verifyException(e, "Trailing token (of type START_ARRAY)");
+            verifyException(e, "value (bound as `com.fasterxml.jackson.databind.JsonNode`)");
+        }
+
+        try {
+            strict.readValue(JSON_FAIL_ARRAY, List.class);
+            fail("Should not have passed");
+        } catch (MismatchedInputException e) {
+            verifyException(e, "Trailing token (of type START_ARRAY)");
+            verifyException(e, "value (bound as `java.util.List`)");
+        }
+
+        // others fail conditionally: will fail on comments unless enabled
+
+        try {
+            strict.readValue(JSON_OK_ARRAY_WITH_COMMENT, List.class);
+            fail("Should not have passed");
+        } catch (JsonParseException e) {
+            verifyException(e, "Unexpected character");
+            verifyException(e, "maybe a (non-standard) comment");
+        }
+        try {
+            strict.readTree(JSON_OK_ARRAY_WITH_COMMENT);
+            fail("Should not have passed");
+        } catch (JsonParseException e) {
+            verifyException(e, "Unexpected character");
+            verifyException(e, "maybe a (non-standard) comment");
+        }
+
+        ObjectMapper strictWithComments = strict.copy();
+        strictWithComments.enable(JsonParser.Feature.ALLOW_COMMENTS);
+        _verifyArray(strictWithComments.readTree(JSON_OK_ARRAY_WITH_COMMENT));
+        _verifyCollection(strictWithComments.readValue(JSON_OK_ARRAY_WITH_COMMENT, List.class));
+    }
+
+    public void testReaderAcceptTrailing() throws Exception
+    {
+        ObjectReader R = MAPPER.reader();
+        assertFalse(R.isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS));
+
+        _verifyArray(R.readTree(JSON_OK_ARRAY));
+        _verifyArray(R.readTree(JSON_OK_ARRAY_WITH_COMMENT));
+        _verifyArray(R.readTree(JSON_FAIL_ARRAY));
+        ObjectReader rColl = R.forType(List.class);
+        _verifyCollection((List<?>)rColl.readValue(JSON_OK_ARRAY));
+        _verifyCollection((List<?>)rColl.readValue(JSON_OK_ARRAY_WITH_COMMENT));
+        _verifyCollection((List<?>)rColl.readValue(JSON_FAIL_ARRAY));
+    }
+
+    public void testReaderFailOnTrailing() throws Exception
+    {
+        ObjectReader strictR = MAPPER.reader().with(DeserializationFeature.FAIL_ON_TRAILING_TOKENS);
+        ObjectReader strictRForList = strictR.forType(List.class);
+        _verifyArray(strictR.readTree(JSON_OK_ARRAY));
+        _verifyCollection((List<?>)strictRForList.readValue(JSON_OK_ARRAY));
+
+        // Will fail hard if there is a trailing token
+        try {
+            strictRForList.readValue(JSON_FAIL_ARRAY);
+            fail("Should not have passed");
+        } catch (MismatchedInputException e) {
+            verifyException(e, "Trailing token (of type START_ARRAY)");
+            verifyException(e, "value (bound as `java.util.List`)");
+        }
+        try {
+            strictR.readTree(JSON_FAIL_ARRAY);
+            fail("Should not have passed");
+        } catch (MismatchedInputException e) {
+            verifyException(e, "Trailing token (of type START_ARRAY)");
+            verifyException(e, "value (bound as `com.fasterxml.jackson.databind.JsonNode`)");
+        }
+
+        // ... also verify that same happens with "value to update"
+        try {
+            strictR.withValueToUpdate(new ArrayList<Object>())
+                .readValue(JSON_FAIL_ARRAY);
+            fail("Should not have passed");
+        } catch (MismatchedInputException e) {
+            verifyException(e, "Trailing token (of type START_ARRAY)");
+            verifyException(e, "value (bound as `java.util.ArrayList`)");
+        }
+
+        // others conditionally: will fail on comments unless enabled
+
+        try {
+            strictRForList.readValue(JSON_OK_ARRAY_WITH_COMMENT);
+            fail("Should not have passed");
+        } catch (JsonParseException e) {
+            verifyException(e, "Unexpected character");
+            verifyException(e, "maybe a (non-standard) comment");
+        }
+        try {
+            strictR.readTree(JSON_OK_ARRAY_WITH_COMMENT);
+            fail("Should not have passed");
+        } catch (JsonParseException e) {
+            verifyException(e, "Unexpected character");
+            verifyException(e, "maybe a (non-standard) comment");
+        }
+
+        // but works if comments enabled etc
+
+        ObjectReader strictRWithComments = strictR.with(JsonParser.Feature.ALLOW_COMMENTS);
+        
+        _verifyCollection((List<?>)strictRWithComments.forType(List.class).readValue(JSON_OK_ARRAY_WITH_COMMENT));
+        _verifyArray(strictRWithComments.readTree(JSON_OK_ARRAY_WITH_COMMENT));
+    }
+
+    private void _verifyArray(JsonNode n) throws Exception
+    {
+        assertTrue(n.isArray());
+        assertEquals(3, n.size());
+    }
+
+    private void _verifyCollection(List<?> coll) throws Exception
+    {
+        assertEquals(3, coll.size());
+        assertEquals(Integer.valueOf(1), coll.get(0));
+        assertEquals(Integer.valueOf(2), coll.get(1));
+        assertEquals(Integer.valueOf(3), coll.get(2));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/TestParserUsingMapper.java b/src/test/java/com/fasterxml/jackson/databind/MapperViaParserTest.java
similarity index 85%
rename from src/test/java/com/fasterxml/jackson/databind/TestParserUsingMapper.java
rename to src/test/java/com/fasterxml/jackson/databind/MapperViaParserTest.java
index 1b8f6b1..54400df 100644
--- a/src/test/java/com/fasterxml/jackson/databind/TestParserUsingMapper.java
+++ b/src/test/java/com/fasterxml/jackson/databind/MapperViaParserTest.java
@@ -8,7 +8,7 @@
 import com.fasterxml.jackson.core.io.SerializedString;
 import com.fasterxml.jackson.databind.ObjectMapper;
 
-public class TestParserUsingMapper  extends BaseMapTest
+public class MapperViaParserTest  extends BaseMapTest
 {
     final static int TWO_BYTE_ESCAPED = 0x111;
     final static int THREE_BYTE_ESCAPED = 0x1111;
@@ -72,27 +72,6 @@
     /********************************************************
      */
 
-    public void testReadingArrayAsTree() throws IOException
-    {
-        JsonFactory jf = new MappingJsonFactory();
-        final String JSON = "[ 1, 2, false ]";
-
-        for (int i = 0; i < 2; ++i) {
-            JsonParser jp = jf.createParser(new StringReader(JSON));
-            // whether to try advancing first or not? Try both
-            if (i == 0) {
-                assertToken(JsonToken.START_ARRAY, jp.nextToken());
-            }
-            JsonNode root = (JsonNode) jp.readValueAsTree();
-            jp.close();
-            assertTrue(root.isArray());
-            assertEquals(3, root.size());
-            assertEquals(1, root.get(0).intValue());
-            assertEquals(2, root.get(1).intValue());
-            assertFalse(root.get(2).booleanValue());
-        }
-    }
-    
     public void testPojoReading() throws IOException
     {
         JsonFactory jf = new MappingJsonFactory();
diff --git a/src/test/java/com/fasterxml/jackson/databind/ObjectMapperTest.java b/src/test/java/com/fasterxml/jackson/databind/ObjectMapperTest.java
index 02a5d76..f59eabc 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ObjectMapperTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ObjectMapperTest.java
@@ -3,14 +3,16 @@
 import java.io.*;
 import java.util.*;
 
+import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonSetter;
+import com.fasterxml.jackson.annotation.Nulls;
 import com.fasterxml.jackson.core.*;
-
 import com.fasterxml.jackson.core.util.MinimalPrettyPrinter;
 
-import com.fasterxml.jackson.databind.deser.DefaultDeserializationContext;
 import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
+import com.fasterxml.jackson.databind.introspect.VisibilityChecker;
 import com.fasterxml.jackson.databind.node.*;
-import com.fasterxml.jackson.databind.type.TypeFactory;
 
 public class ObjectMapperTest extends BaseMapTest
 {
@@ -18,19 +20,12 @@
         int value = 3;
         
         public void setX(int v) { value = v; }
+
+        protected Bean() { }
+        public Bean(int v) { value = v; }
     }
 
     static class EmptyBean { }
-    
-    // for [databind#206]
-    @SuppressWarnings("serial")
-    static class CustomMapper extends ObjectMapper {
-        @Override
-        protected DefaultDeserializationContext createDeserializationContext(JsonParser jp,
-                DeserializationConfig cfg) {
-            return super.createDeserializationContext(jp, cfg);
-        }
-    }
 
     @SuppressWarnings("serial")
     static class MyAnnotationIntrospector extends JacksonAnnotationIntrospector { }
@@ -48,15 +43,166 @@
             g.writeRaw(" , ");
         }
     }
-    
+
+    // for [databind#206]
+    @SuppressWarnings("serial")
+    static class NoCopyMapper extends ObjectMapper { }
+
+    final ObjectMapper MAPPER = new ObjectMapper();
+
     /*
     /**********************************************************
-    /* Test methods
+    /* Test methods, config
     /**********************************************************
      */
-    
-    final static ObjectMapper MAPPER = new ObjectMapper();
-    
+
+    public void testFactorFeatures()
+    {
+        assertTrue(MAPPER.isEnabled(JsonFactory.Feature.CANONICALIZE_FIELD_NAMES));
+    }
+
+    public void testGeneratorFeatures()
+    {
+        // and also for mapper
+        ObjectMapper mapper = new ObjectMapper();
+        assertFalse(mapper.isEnabled(JsonGenerator.Feature.ESCAPE_NON_ASCII));
+        assertTrue(mapper.isEnabled(JsonGenerator.Feature.QUOTE_FIELD_NAMES));
+        mapper.disable(JsonGenerator.Feature.FLUSH_PASSED_TO_STREAM,
+                JsonGenerator.Feature.QUOTE_FIELD_NAMES);
+    }
+
+    public void testParserFeatures()
+    {
+        // and also for mapper
+        ObjectMapper mapper = new ObjectMapper();
+                
+        assertTrue(mapper.isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE));
+        assertFalse(mapper.isEnabled(JsonParser.Feature.ALLOW_COMMENTS));
+
+        mapper.disable(JsonParser.Feature.AUTO_CLOSE_SOURCE,
+                JsonParser.Feature.STRICT_DUPLICATE_DETECTION);
+        assertFalse(mapper.isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE));
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods, mapper.copy()
+    /**********************************************************
+     */
+
+    // [databind#28]: ObjectMapper.copy()
+    public void testCopy() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        assertTrue(m.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));
+        m.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
+        assertFalse(m.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));
+        InjectableValues inj = new InjectableValues.Std();
+        m.setInjectableValues(inj);
+        assertFalse(m.isEnabled(JsonParser.Feature.ALLOW_COMMENTS));
+        m.enable(JsonParser.Feature.ALLOW_COMMENTS);
+        assertTrue(m.isEnabled(JsonParser.Feature.ALLOW_COMMENTS));
+
+        // // First: verify that handling of features is decoupled:
+        
+        ObjectMapper m2 = m.copy();
+        assertFalse(m2.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));
+        m2.enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
+        assertTrue(m2.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));
+        assertSame(inj, m2.getInjectableValues());
+
+        // but should NOT change the original
+        assertFalse(m.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));
+
+        // nor vice versa:
+        assertFalse(m.isEnabled(DeserializationFeature.UNWRAP_ROOT_VALUE));
+        assertFalse(m2.isEnabled(DeserializationFeature.UNWRAP_ROOT_VALUE));
+        m.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
+        assertTrue(m.isEnabled(DeserializationFeature.UNWRAP_ROOT_VALUE));
+        assertFalse(m2.isEnabled(DeserializationFeature.UNWRAP_ROOT_VALUE));
+
+        // // Also, underlying JsonFactory instances should be distinct
+        
+        assertNotSame(m.getFactory(), m2.getFactory());
+
+        // [databind#122]: Need to ensure mix-ins are not shared
+        assertEquals(0, m.getSerializationConfig().mixInCount());
+        assertEquals(0, m2.getSerializationConfig().mixInCount());
+        assertEquals(0, m.getDeserializationConfig().mixInCount());
+        assertEquals(0, m2.getDeserializationConfig().mixInCount());
+
+        m.addMixIn(String.class, Integer.class);
+        assertEquals(1, m.getSerializationConfig().mixInCount());
+        assertEquals(0, m2.getSerializationConfig().mixInCount());
+        assertEquals(1, m.getDeserializationConfig().mixInCount());
+        assertEquals(0, m2.getDeserializationConfig().mixInCount());
+
+        // [databind#913]: Ensure JsonFactory Features copied
+        assertTrue(m2.isEnabled(JsonParser.Feature.ALLOW_COMMENTS));
+    }
+
+    // [databind#1580]
+    public void testCopyOfConfigOverrides() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        SerializationConfig config = m.getSerializationConfig();
+        assertEquals(JsonInclude.Value.empty(), config.getDefaultPropertyInclusion());
+        assertEquals(JsonSetter.Value.empty(), config.getDefaultSetterInfo());
+        assertNull(config.getDefaultMergeable());
+        VisibilityChecker<?> defaultVis = config.getDefaultVisibilityChecker();
+        assertEquals(VisibilityChecker.Std.class, defaultVis.getClass());
+
+        // change
+        JsonInclude.Value customIncl = JsonInclude.Value.empty().withValueInclusion(JsonInclude.Include.NON_DEFAULT);
+        m.setDefaultPropertyInclusion(customIncl);
+        JsonSetter.Value customSetter = JsonSetter.Value.forValueNulls(Nulls.SKIP);
+        m.setDefaultSetterInfo(customSetter);
+        m.setDefaultMergeable(Boolean.TRUE);
+        VisibilityChecker<?> customVis = VisibilityChecker.Std.defaultInstance()
+                .withFieldVisibility(Visibility.ANY);
+        m.setVisibility(customVis);
+        assertSame(customVis, m.getVisibilityChecker());
+
+        // and verify that copy retains these settings
+        ObjectMapper m2 = m.copy();
+        SerializationConfig config2 = m2.getSerializationConfig();
+        assertSame(customIncl, config2.getDefaultPropertyInclusion());
+        assertSame(customSetter, config2.getDefaultSetterInfo());
+        assertEquals(Boolean.TRUE, config2.getDefaultMergeable());
+        assertSame(customVis, config2.getDefaultVisibilityChecker());
+    }
+
+    public void testFailedCopy() throws Exception
+    {
+        NoCopyMapper src = new NoCopyMapper();
+        try {
+            src.copy();
+            fail("Should not pass");
+        } catch (IllegalStateException e) {
+            verifyException(e, "does not override copy()");
+        }
+    }
+
+    public void testAnnotationIntrospectorCopyin() 
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.setAnnotationIntrospector(new MyAnnotationIntrospector());
+        assertEquals(MyAnnotationIntrospector.class,
+                m.getDeserializationConfig().getAnnotationIntrospector().getClass());
+        ObjectMapper m2 = m.copy();
+
+        assertEquals(MyAnnotationIntrospector.class,
+                m2.getDeserializationConfig().getAnnotationIntrospector().getClass());
+        assertEquals(MyAnnotationIntrospector.class,
+                m2.getSerializationConfig().getAnnotationIntrospector().getClass());
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods, other
+    /**********************************************************
+     */
+
     public void testProps()
     {
         ObjectMapper m = new ObjectMapper();
@@ -68,25 +214,6 @@
         assertSame(nf, m.getNodeFactory());
     }
 
-    public void testSupport()
-    {
-        assertTrue(MAPPER.canSerialize(String.class));
-        assertTrue(MAPPER.canDeserialize(TypeFactory.defaultInstance().constructType(String.class)));
-    }
-
-    public void testTreeRead() throws Exception
-    {
-        String JSON = "{ }";
-        JsonNode n = MAPPER.readTree(JSON);
-        assertTrue(n instanceof ObjectNode);
-
-        n = MAPPER.readTree(new StringReader(JSON));
-        assertTrue(n instanceof ObjectNode);
-
-        n = MAPPER.readTree(new ByteArrayInputStream(JSON.getBytes("UTF-8")));
-        assertTrue(n instanceof ObjectNode);
-    }
-
     // Test to ensure that we can check property ordering defaults...
     public void testConfigForPropertySorting() throws Exception
     {
@@ -123,10 +250,7 @@
         assertSame(f, m.getFactory());
         assertSame(m, f.getCodec());
     }
-    
-    /**
-     * Test for verifying working of [JACKSON-191]
-     */
+
     public void testProviderConfig() throws Exception   
     {
         ObjectMapper m = new ObjectMapper();
@@ -149,72 +273,6 @@
         // 4 deserializers (int, List<?>, Map<?,?>, Object)
         assertEquals(4, m._deserializationContext._cache.cachedDeserializersCount());
     }
-    
-    // [databind#28]: ObjectMapper.copy()
-    public void testCopy() throws Exception
-    {
-        ObjectMapper m = new ObjectMapper();
-        assertTrue(m.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));
-        m.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
-        assertFalse(m.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));
-        InjectableValues inj = new InjectableValues.Std();
-        m.setInjectableValues(inj);
-        assertFalse(m.isEnabled(JsonParser.Feature.ALLOW_COMMENTS));
-        m.enable(JsonParser.Feature.ALLOW_COMMENTS);
-        assertTrue(m.isEnabled(JsonParser.Feature.ALLOW_COMMENTS));
-
-        // // First: verify that handling of features is decoupled:
-        
-        ObjectMapper m2 = m.copy();
-        assertFalse(m2.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));
-        m2.enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
-        assertTrue(m2.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));
-        assertSame(inj, m2.getInjectableValues());
-
-        // but should NOT change the original
-        assertFalse(m.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));
-
-        // nor vice versa:
-        assertFalse(m.isEnabled(DeserializationFeature.UNWRAP_ROOT_VALUE));
-        assertFalse(m2.isEnabled(DeserializationFeature.UNWRAP_ROOT_VALUE));
-        m.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
-        assertTrue(m.isEnabled(DeserializationFeature.UNWRAP_ROOT_VALUE));
-        assertFalse(m2.isEnabled(DeserializationFeature.UNWRAP_ROOT_VALUE));
-
-        // // Also, underlying JsonFactory instances should be distinct
-        
-        assertNotSame(m.getFactory(), m2.getFactory());
-
-        // [Issue#122]: Need to ensure mix-ins are not shared
-        assertEquals(0, m.getSerializationConfig().mixInCount());
-        assertEquals(0, m2.getSerializationConfig().mixInCount());
-        assertEquals(0, m.getDeserializationConfig().mixInCount());
-        assertEquals(0, m2.getDeserializationConfig().mixInCount());
-
-        m.addMixIn(String.class, Integer.class);
-        assertEquals(1, m.getSerializationConfig().mixInCount());
-        assertEquals(0, m2.getSerializationConfig().mixInCount());
-        assertEquals(1, m.getDeserializationConfig().mixInCount());
-        assertEquals(0, m2.getDeserializationConfig().mixInCount());
-
-        // [Issue#913]: Ensure JsonFactory Features copied
-        assertTrue(m2.isEnabled(JsonParser.Feature.ALLOW_COMMENTS));
-        
-    }
-
-    public void testAnnotationIntrospectorCopyin() 
-    {
-        ObjectMapper m = new ObjectMapper();
-        m.setAnnotationIntrospector(new MyAnnotationIntrospector());
-        assertEquals(MyAnnotationIntrospector.class,
-                m.getDeserializationConfig().getAnnotationIntrospector().getClass());
-        ObjectMapper m2 = m.copy();
-
-        assertEquals(MyAnnotationIntrospector.class,
-                m2.getDeserializationConfig().getAnnotationIntrospector().getClass());
-        assertEquals(MyAnnotationIntrospector.class,
-                m2.getSerializationConfig().getAnnotationIntrospector().getClass());
-    }
 
     // For [databind#689]
     public void testCustomDefaultPrettyPrinter() throws Exception
diff --git a/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java b/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java
new file mode 100644
index 0000000..1d67d55
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java
@@ -0,0 +1,327 @@
+package com.fasterxml.jackson.databind;
+
+import java.io.StringWriter;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.cfg.ContextAttributes;
+import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+public class ObjectReaderTest extends BaseMapTest
+{
+    final ObjectMapper MAPPER = new ObjectMapper();
+
+    static class POJO {
+        public Map<String, Object> name;
+    }
+
+    public void testSimpleViaParser() throws Exception
+    {
+        final String JSON = "[1]";
+        JsonParser p = MAPPER.getFactory().createParser(JSON);
+        Object ob = MAPPER.readerFor(Object.class)
+                .readValue(p);
+        p.close();
+        assertTrue(ob instanceof List<?>);
+    }
+
+    public void testSimpleAltSources() throws Exception
+    {
+        final String JSON = "[1]";
+        final byte[] BYTES = JSON.getBytes("UTF-8");
+        Object ob = MAPPER.readerFor(Object.class)
+                .readValue(BYTES);
+        assertTrue(ob instanceof List<?>);
+
+        ob = MAPPER.readerFor(Object.class)
+                .readValue(BYTES, 0, BYTES.length);
+        assertTrue(ob instanceof List<?>);
+        assertEquals(1, ((List<?>) ob).size());
+    }
+
+    public void testParserFeatures() throws Exception
+    {
+        final String JSON = "[ /* foo */ 7 ]";
+        // default won't accept comments, let's change that:
+        ObjectReader reader = MAPPER.readerFor(int[].class)
+                .with(JsonParser.Feature.ALLOW_COMMENTS);
+
+        int[] value = reader.readValue(JSON);
+        assertNotNull(value);
+        assertEquals(1, value.length);
+        assertEquals(7, value[0]);
+
+        // but also can go back
+        try {
+            reader.without(JsonParser.Feature.ALLOW_COMMENTS).readValue(JSON);
+            fail("Should not have passed");
+        } catch (JsonProcessingException e) {
+            verifyException(e, "foo");
+        }
+    }
+
+    public void testNodeHandling() throws Exception
+    {
+        JsonNodeFactory nodes = new JsonNodeFactory(true);
+        ObjectReader r = MAPPER.reader().with(nodes);
+        // but also no further changes if attempting again
+        assertSame(r, r.with(nodes));
+        assertTrue(r.createArrayNode().isArray());
+        assertTrue(r.createObjectNode().isObject());
+    }
+
+    public void testFeatureSettings() throws Exception
+    {
+        ObjectReader r = MAPPER.reader();
+        assertFalse(r.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES));
+        assertFalse(r.isEnabled(JsonParser.Feature.ALLOW_COMMENTS));
+        
+        r = r.withoutFeatures(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES,
+                DeserializationFeature.FAIL_ON_INVALID_SUBTYPE);
+        assertFalse(r.isEnabled(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES));
+        assertFalse(r.isEnabled(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE));
+        r = r.withFeatures(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES,
+                DeserializationFeature.FAIL_ON_INVALID_SUBTYPE);
+        assertTrue(r.isEnabled(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES));
+        assertTrue(r.isEnabled(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE));
+
+        // alternative method too... can't recall why two
+        assertSame(r, r.with(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES,
+                DeserializationFeature.FAIL_ON_INVALID_SUBTYPE));
+    }
+
+    public void testMiscSettings() throws Exception
+    {
+        ObjectReader r = MAPPER.reader();
+        assertSame(MAPPER.getFactory(), r.getFactory());
+
+        JsonFactory f = new JsonFactory();
+        r = r.with(f);
+        assertSame(f, r.getFactory());
+        assertSame(r, r.with(f));
+
+        assertNotNull(r.getTypeFactory());
+        assertNull(r.getInjectableValues());
+
+        r = r.withAttributes(Collections.emptyMap());
+        ContextAttributes attrs = r.getAttributes();
+        assertNotNull(attrs);
+        assertNull(attrs.getAttribute("abc"));
+        assertSame(r, r.withoutAttribute("foo"));
+
+        ObjectReader newR = r.forType(MAPPER.constructType(String.class));
+        assertNotSame(r, newR);
+        assertSame(newR, newR.forType(String.class));
+
+        DeserializationProblemHandler probH = new DeserializationProblemHandler() {
+        };
+        newR = r.withHandler(probH);
+        assertNotSame(r, newR);
+        assertSame(newR, newR.withHandler(probH));
+        r = newR;
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testDeprecatedSettings() throws Exception
+    {
+        ObjectReader r = MAPPER.reader();
+
+        // and deprecated variants
+        ObjectReader newR = r.forType(MAPPER.constructType(String.class));
+        assertSame(newR, newR.withType(String.class));
+        assertSame(newR, newR.withType(MAPPER.constructType(String.class)));
+
+        newR = newR.withRootName(PropertyName.construct("foo"));
+        assertNotSame(r, newR);
+        assertSame(newR, newR.withRootName(PropertyName.construct("foo")));
+    }
+
+    public void testNoPrefetch() throws Exception
+    {
+        ObjectReader r = MAPPER.reader()
+                .without(DeserializationFeature.EAGER_DESERIALIZER_FETCH);
+        Number n = r.forType(Integer.class).readValue("123 ");
+        assertEquals(Integer.valueOf(123), n);
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods, JsonPointer
+    /**********************************************************
+     */
+
+    public void testNoPointerLoading() throws Exception {
+        final String source = "{\"foo\":{\"bar\":{\"caller\":{\"name\":{\"value\":1234}}}}}";
+
+        JsonNode tree = MAPPER.readTree(source);
+        JsonNode node = tree.at("/foo/bar/caller");
+        POJO pojo = MAPPER.treeToValue(node, POJO.class);
+        assertTrue(pojo.name.containsKey("value"));
+        assertEquals(1234, pojo.name.get("value"));
+    }
+
+    public void testPointerLoading() throws Exception {
+        final String source = "{\"foo\":{\"bar\":{\"caller\":{\"name\":{\"value\":1234}}}}}";
+
+        ObjectReader reader = MAPPER.readerFor(POJO.class).at("/foo/bar/caller");
+
+        POJO pojo = reader.readValue(source);
+        assertTrue(pojo.name.containsKey("value"));
+        assertEquals(1234, pojo.name.get("value"));
+    }
+
+    public void testPointerLoadingAsJsonNode() throws Exception {
+        final String source = "{\"foo\":{\"bar\":{\"caller\":{\"name\":{\"value\":1234}}}}}";
+
+        ObjectReader reader = MAPPER.readerFor(POJO.class).at(JsonPointer.compile("/foo/bar/caller"));
+
+        JsonNode node = reader.readTree(source);
+        assertTrue(node.has("name"));
+        assertEquals("{\"value\":1234}", node.get("name").toString());
+    }
+
+    public void testPointerLoadingMappingIteratorOne() throws Exception {
+        final String source = "{\"foo\":{\"bar\":{\"caller\":{\"name\":{\"value\":1234}}}}}";
+
+        ObjectReader reader = MAPPER.readerFor(POJO.class).at("/foo/bar/caller");
+
+        MappingIterator<POJO> itr = reader.readValues(source);
+
+        POJO pojo = itr.next();
+
+        assertTrue(pojo.name.containsKey("value"));
+        assertEquals(1234, pojo.name.get("value"));
+        assertFalse(itr.hasNext());
+        itr.close();
+    }
+    
+    public void testPointerLoadingMappingIteratorMany() throws Exception {
+        final String source = "{\"foo\":{\"bar\":{\"caller\":[{\"name\":{\"value\":1234}}, {\"name\":{\"value\":5678}}]}}}";
+
+        ObjectReader reader = MAPPER.readerFor(POJO.class).at("/foo/bar/caller");
+
+        MappingIterator<POJO> itr = reader.readValues(source);
+
+        POJO pojo = itr.next();
+
+        assertTrue(pojo.name.containsKey("value"));
+        assertEquals(1234, pojo.name.get("value"));
+        assertTrue(itr.hasNext());
+        
+        pojo = itr.next();
+
+        assertNotNull(pojo.name);
+        assertTrue(pojo.name.containsKey("value"));
+        assertEquals(5678, pojo.name.get("value"));
+        assertFalse(itr.hasNext());
+        itr.close();
+    }
+
+    // [databind#1637]
+    public void testPointerWithArrays() throws Exception
+    {
+        final String json = aposToQuotes("{\n'wrapper1': {\n" +
+                "  'set1': ['one', 'two', 'three'],\n" +
+                "  'set2': ['four', 'five', 'six']\n" +
+                "},\n" +
+                "'wrapper2': {\n" +
+                "  'set1': ['one', 'two', 'three'],\n" +
+                "  'set2': ['four', 'five', 'six']\n" +
+                "}\n}");
+
+        final Pojo1637 testObject = MAPPER.readerFor(Pojo1637.class)
+                .at("/wrapper1")
+                .readValue(json);
+        assertNotNull(testObject);
+
+        assertNotNull(testObject.set1);
+        assertTrue(!testObject.set1.isEmpty());
+
+        assertNotNull(testObject.set2);
+        assertTrue(!testObject.set2.isEmpty());
+    }
+
+    public static class Pojo1637 {
+        public Set<String> set1;
+        public Set<String> set2;
+    }    
+
+    /*
+    /**********************************************************
+    /* Test methods, ObjectCodec
+    /**********************************************************
+     */
+
+    public void testTreeToValue() throws Exception
+    {
+        ArrayNode n = MAPPER.createArrayNode();
+        n.add("xyz");
+        ObjectReader r = MAPPER.readerFor(String.class);
+        List<?> list = r.treeToValue(n, List.class);
+        assertEquals(1, list.size());
+    }
+    
+    public void testCodecUnsupportedWrites() throws Exception
+    {
+        ObjectReader r = MAPPER.readerFor(String.class);
+        JsonGenerator g = MAPPER.getFactory().createGenerator(new StringWriter());
+        ObjectNode n = MAPPER.createObjectNode();
+        try {
+            r.writeTree(g, n);
+            fail("Should not pass");
+        } catch (UnsupportedOperationException e) {
+            ;
+        }
+        try {
+            r.writeValue(g, "Foo");
+            fail("Should not pass");
+        } catch (UnsupportedOperationException e) {
+            ;
+        }
+        g.close();
+
+        g.close();
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods, failures, other
+    /**********************************************************
+     */
+
+    public void testMissingType() throws Exception
+    {
+        ObjectReader r = MAPPER.reader();
+        try {
+            r.readValue("1");
+            fail("Should not pass");
+        } catch (JsonMappingException e) {
+            verifyException(e, "No value type configured");
+        }
+    }
+
+    public void testSchema() throws Exception
+    {
+        ObjectReader r = MAPPER.readerFor(String.class);
+        
+        // Ok to try to set `null` schema, always:
+        r = r.with((FormatSchema) null);
+
+        try {
+            // but not schema that doesn't match format (no schema exists for json)
+            r = r.with(new BogusSchema())
+                .readValue(quote("foo"));
+            
+            fail("Should not pass");
+        } catch (IllegalArgumentException e) {
+            verifyException(e, "Cannot use FormatSchema");
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/seq/ObjectWriterTest.java b/src/test/java/com/fasterxml/jackson/databind/ObjectWriterTest.java
similarity index 60%
rename from src/test/java/com/fasterxml/jackson/databind/seq/ObjectWriterTest.java
rename to src/test/java/com/fasterxml/jackson/databind/ObjectWriterTest.java
index b4cc2a8..b9d2e5d 100644
--- a/src/test/java/com/fasterxml/jackson/databind/seq/ObjectWriterTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ObjectWriterTest.java
@@ -1,8 +1,9 @@
-package com.fasterxml.jackson.databind.seq;
+package com.fasterxml.jackson.databind;
 
 import java.io.ByteArrayOutputStream;
 import java.io.Closeable;
 import java.io.IOException;
+import java.io.StringWriter;
 import java.util.*;
 
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
@@ -10,7 +11,6 @@
 
 import com.fasterxml.jackson.core.*;
 import com.fasterxml.jackson.core.io.SerializedString;
-import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 
 /**
@@ -146,13 +146,32 @@
         assertNotNull(json);
         assertTrue(input.closed);
         input.close();
+
+        // and via explicitly passed generator
+        JsonGenerator g = MAPPER.getFactory().createGenerator(new StringWriter());
+        input = new CloseableValue();
+        assertFalse(input.closed);
+        w.writeValue(g, input);
+        assertTrue(input.closed);
+        g.close();
+        input.close();
     }
 
-    public void testSettings() throws Exception
+    public void testViewSettings() throws Exception
     {
         ObjectWriter w = MAPPER.writer();
-        assertFalse(w.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES));
-        assertFalse(w.isEnabled(JsonGenerator.Feature.STRICT_DUPLICATE_DETECTION));
+        ObjectWriter newW = w.withView(String.class);
+        assertNotSame(w, newW);
+        assertSame(newW, newW.withView(String.class));
+
+        newW = w.with(Locale.CANADA);
+        assertNotSame(w, newW);
+        assertSame(newW, newW.with(Locale.CANADA));
+    }
+
+    public void testMiscSettings() throws Exception
+    {
+        ObjectWriter w = MAPPER.writer();
         assertSame(MAPPER.getFactory(), w.getFactory());
         assertFalse(w.hasPrefetchedSerializer());
         assertNotNull(w.getTypeFactory());
@@ -160,16 +179,96 @@
         JsonFactory f = new JsonFactory();
         w = w.with(f);
         assertSame(f, w.getFactory());
-
-        w = w.withView(String.class);
+        ObjectWriter newW = w.with(Base64Variants.MODIFIED_FOR_URL);
+        assertNotSame(w, newW);
+        assertSame(newW, newW.with(Base64Variants.MODIFIED_FOR_URL));
+        
         w = w.withAttributes(Collections.emptyMap());
         w = w.withAttribute("a", "b");
         assertEquals("b", w.getAttributes().getAttribute("a"));
         w = w.withoutAttribute("a");
         assertNull(w.getAttributes().getAttribute("a"));
-        w = w.withRootValueSeparator(new SerializedString(","));
+
+        FormatSchema schema = new BogusSchema();
+        try {
+            newW = w.with(schema);
+            fail("Should not pass");
+        } catch (IllegalArgumentException e) {
+            verifyException(e, "Cannot use FormatSchema");
+        }
     }
 
+    public void testRootValueSettings() throws Exception
+    {
+        ObjectWriter w = MAPPER.writer();
+        
+        // First, root name:
+        ObjectWriter newW = w.withRootName("foo");
+        assertNotSame(w, newW);
+        assertSame(newW, newW.withRootName(PropertyName.construct("foo")));
+        w = newW;
+        newW = w.withRootName((String) null);
+        assertNotSame(w, newW);
+        assertSame(newW, newW.withRootName((PropertyName) null));
+
+        // Then root value separator
+
+        w = w.withRootValueSeparator(new SerializedString(","));
+        assertSame(w, w.withRootValueSeparator(new SerializedString(",")));
+        assertSame(w, w.withRootValueSeparator(","));
+
+         newW = w.withRootValueSeparator("/");
+        assertNotSame(w, newW);
+        assertSame(newW, newW.withRootValueSeparator("/"));
+
+        newW = w.withRootValueSeparator((String) null);
+        assertNotSame(w, newW);
+
+        newW = w.withRootValueSeparator((SerializableString) null);
+        assertNotSame(w, newW);
+    }
+
+    public void testFeatureSettings() throws Exception
+    {
+        ObjectWriter w = MAPPER.writer();
+        assertFalse(w.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES));
+        assertFalse(w.isEnabled(JsonGenerator.Feature.STRICT_DUPLICATE_DETECTION));
+        ObjectWriter newW = w.with(SerializationFeature.FAIL_ON_UNWRAPPED_TYPE_IDENTIFIERS,
+                SerializationFeature.INDENT_OUTPUT);
+        assertNotSame(w, newW);
+        assertTrue(newW.isEnabled(SerializationFeature.FAIL_ON_UNWRAPPED_TYPE_IDENTIFIERS));
+        assertTrue(newW.isEnabled(SerializationFeature.INDENT_OUTPUT));
+        assertSame(newW, newW.with(SerializationFeature.INDENT_OUTPUT));
+        assertSame(newW, newW.withFeatures(SerializationFeature.INDENT_OUTPUT));
+
+        newW = w.withFeatures(SerializationFeature.FAIL_ON_UNWRAPPED_TYPE_IDENTIFIERS,
+                SerializationFeature.INDENT_OUTPUT);
+        assertNotSame(w, newW);
+
+        newW = w.without(SerializationFeature.FAIL_ON_EMPTY_BEANS,
+                SerializationFeature.EAGER_SERIALIZER_FETCH);
+        assertNotSame(w, newW);
+        assertFalse(newW.isEnabled(SerializationFeature.FAIL_ON_EMPTY_BEANS));
+        assertFalse(newW.isEnabled(SerializationFeature.EAGER_SERIALIZER_FETCH));
+        assertSame(newW, newW.without(SerializationFeature.FAIL_ON_EMPTY_BEANS));
+        assertSame(newW, newW.withoutFeatures(SerializationFeature.FAIL_ON_EMPTY_BEANS));
+
+        assertNotSame(w, w.withoutFeatures(SerializationFeature.FAIL_ON_EMPTY_BEANS,
+                SerializationFeature.EAGER_SERIALIZER_FETCH));
+    }
+
+    public void testGeneratorFeatures() throws Exception
+    {
+        ObjectWriter w = MAPPER.writer();
+        assertFalse(w.isEnabled(JsonGenerator.Feature.ESCAPE_NON_ASCII));
+        assertNotSame(w, w.with(JsonGenerator.Feature.ESCAPE_NON_ASCII));
+        assertNotSame(w, w.withFeatures(JsonGenerator.Feature.ESCAPE_NON_ASCII));
+
+        assertTrue(w.isEnabled(JsonGenerator.Feature.AUTO_CLOSE_TARGET));
+        assertNotSame(w, w.without(JsonGenerator.Feature.AUTO_CLOSE_TARGET));
+        assertNotSame(w, w.withoutFeatures(JsonGenerator.Feature.AUTO_CLOSE_TARGET));
+    }
+    
     /*
     /**********************************************************
     /* Test methods, failures
@@ -195,7 +294,7 @@
                 .writeValueAsBytes("foo");
             fail("Should not pass");
         } catch (IllegalArgumentException e) {
-            verifyException(e, "Can not use FormatSchema");
+            verifyException(e, "Cannot use FormatSchema");
         }
     }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/TestGeneratorUsingMapper.java b/src/test/java/com/fasterxml/jackson/databind/TestGeneratorUsingMapper.java
deleted file mode 100644
index 78955c8..0000000
--- a/src/test/java/com/fasterxml/jackson/databind/TestGeneratorUsingMapper.java
+++ /dev/null
@@ -1,84 +0,0 @@
-package com.fasterxml.jackson.databind;
-
-import java.io.IOException;
-import java.io.StringWriter;
-
-import com.fasterxml.jackson.core.JsonFactory;
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.core.SerializableString;
-import com.fasterxml.jackson.core.io.CharacterEscapes;
-
-public class TestGeneratorUsingMapper extends BaseMapTest
-{
-    final static class Pojo
-    {
-        public int getX() { return 4; }
-    }
-
-    /*
-    /**********************************************************
-    /* Tests for data binding integration
-    /**********************************************************
-     */
-
-    public void testPojoWriting()
-        throws IOException
-    {
-        JsonFactory jf = new MappingJsonFactory();
-        StringWriter sw = new StringWriter();
-        JsonGenerator gen = jf.createGenerator(sw);
-        gen.writeObject(new Pojo());
-        gen.close();
-        // trimming needed if main-level object has leading space
-        String act = sw.toString().trim();
-        assertEquals("{\"x\":4}", act);
-    }
-
-    public void testPojoWritingFailing()
-        throws IOException
-    {
-        // regular factory can't do it, without a call to setCodec()
-        JsonFactory jf = new JsonFactory();
-        try {
-            StringWriter sw = new StringWriter();
-            JsonGenerator gen = jf.createGenerator(sw);
-            gen.writeObject(new Pojo());
-            gen.close();
-            fail("Expected an exception: got sw '"+sw.toString()+"'");
-        } catch (IllegalStateException e) {
-            verifyException(e, "No ObjectCodec defined");
-        }
-    }
-
-    public void testIssue820() throws IOException
-    {
-        StringBuffer sb = new StringBuffer();
-        while (sb.length() <= 5000) {
-            sb.append("Yet another line of text...\n");
-        }
-        String sampleText = sb.toString();
-        assertTrue(
-                "Sanity check so I don't mess up the sample text later.",
-                sampleText.contains("\n"));
-
-        final ObjectMapper mapper = new ObjectMapper();
-        final CharacterEscapes defaultCharacterEscapes = new CharacterEscapes() {
-            private static final long serialVersionUID = 1L;
-
-            @Override
-            public int[] getEscapeCodesForAscii() {
-                return standardAsciiEscapesForJSON();
-            }
-
-            @Override
-            public SerializableString getEscapeSequence(final int ch) {
-                return null;
-            }
-        };
-
-        mapper.getFactory().setCharacterEscapes(defaultCharacterEscapes);
-        String jacksonJson = mapper.writeValueAsString(sampleText);
-        boolean hasLFs = jacksonJson.indexOf('\n') > 0;
-        assertFalse("Should NOT contain linefeeds, should have been escaped", hasLFs);
-    }    
-}
diff --git a/src/test/java/com/fasterxml/jackson/databind/TestJDKSerialization.java b/src/test/java/com/fasterxml/jackson/databind/TestJDKSerialization.java
index dd5c4fe..d42abe0 100644
--- a/src/test/java/com/fasterxml/jackson/databind/TestJDKSerialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/TestJDKSerialization.java
@@ -3,6 +3,14 @@
 import java.io.*;
 import java.util.*;
 
+import com.fasterxml.jackson.annotation.JsonAnyGetter;
+import com.fasterxml.jackson.annotation.JsonAnySetter;
+import com.fasterxml.jackson.databind.DeserializationConfig;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectReader;
+import com.fasterxml.jackson.databind.ObjectWriter;
+import com.fasterxml.jackson.databind.SerializationConfig;
 import com.fasterxml.jackson.databind.type.TypeFactory;
 import com.fasterxml.jackson.databind.util.LRUMap;
 
@@ -35,6 +43,25 @@
         public Map<String,ABC> stuff = new LinkedHashMap<String,ABC>();
     }
 
+    static class AnyBean {
+        HashMap<String,Object> _map;
+
+        public AnyBean() {
+            _map = new HashMap<String,Object>();
+        }
+
+        @JsonAnySetter
+        AnyBean addEntry(String key, Object value) {
+            _map.put(key, value);
+            return this;
+        }
+
+        @JsonAnyGetter
+        public Map<String,Object> properties() {
+            return _map;
+        }
+    }
+
     /*
     /**********************************************************
     /* Tests for individual objects
@@ -107,6 +134,9 @@
         final String EXP_JSON = "{\"x\":2,\"y\":3}";
         final MyPojo p = new MyPojo(2, 3);
         assertEquals(EXP_JSON, origWriter.writeValueAsString(p));
+        String json = origWriter.writeValueAsString(new AnyBean()
+                .addEntry("a", "b"));
+        assertNotNull(json);
         byte[] bytes = jdkSerialize(origWriter);
         ObjectWriter writer2 = jdkDeserialize(bytes);
         assertEquals(EXP_JSON, writer2.writeValueAsString(p));
@@ -115,13 +145,21 @@
     public void testObjectReader() throws IOException
     {
         ObjectReader origReader = MAPPER.readerFor(MyPojo.class);
-        final String JSON = "{\"x\":1,\"y\":2}";
+        String JSON = "{\"x\":1,\"y\":2}";
         MyPojo p1 = origReader.readValue(JSON);
         assertEquals(2, p1.y);
-        byte[] bytes = jdkSerialize(origReader);
-        ObjectReader reader2 = jdkDeserialize(bytes);
+        ObjectReader anyReader = MAPPER.readerFor(AnyBean.class);
+        AnyBean any = anyReader.readValue(JSON);
+        assertEquals(Integer.valueOf(2), any.properties().get("y"));
+        
+        byte[] readerBytes = jdkSerialize(origReader);
+        ObjectReader reader2 = jdkDeserialize(readerBytes);
         MyPojo p2 = reader2.readValue(JSON);
         assertEquals(2, p2.y);
+
+        ObjectReader anyReader2 = jdkDeserialize(jdkSerialize(anyReader));
+        AnyBean any2 = anyReader2.readValue(JSON);
+        assertEquals(Integer.valueOf(2), any2.properties().get("y"));
     }
 
     public void testObjectMapper() throws IOException
@@ -129,6 +167,8 @@
         final String EXP_JSON = "{\"x\":2,\"y\":3}";
         final MyPojo p = new MyPojo(2, 3);
         assertEquals(EXP_JSON, MAPPER.writeValueAsString(p));
+        assertNotNull(MAPPER.getFactory());
+        assertNotNull(MAPPER.getFactory().getCodec());
 
         byte[] bytes = jdkSerialize(MAPPER);
         ObjectMapper mapper2 = jdkDeserialize(bytes);
@@ -136,6 +176,10 @@
         MyPojo p2 = mapper2.readValue(EXP_JSON, MyPojo.class);
         assertEquals(p.x, p2.x);
         assertEquals(p.y, p2.y);
+
+        // [databind#2038]: verify that codec is not lost
+        assertNotNull(mapper2.getFactory());
+        assertNotNull(mapper2.getFactory().getCodec());
     }
 
     public void testTypeFactory() throws Exception
diff --git a/src/test/java/com/fasterxml/jackson/databind/TestObjectMapperBeanDeserializer.java b/src/test/java/com/fasterxml/jackson/databind/TestObjectMapperBeanDeserializer.java
deleted file mode 100644
index 5d96b07..0000000
--- a/src/test/java/com/fasterxml/jackson/databind/TestObjectMapperBeanDeserializer.java
+++ /dev/null
@@ -1,329 +0,0 @@
-package com.fasterxml.jackson.databind;
-
-import java.io.*;
-import java.net.URI;
-import java.util.*;
-
-import com.fasterxml.jackson.core.*;
-import com.fasterxml.jackson.databind.JsonMappingException;
-import com.fasterxml.jackson.databind.JsonSerializable;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.SerializerProvider;
-import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
-
-/**
- * Unit tests for verifying deserialization of Beans.
- */
-public class TestObjectMapperBeanDeserializer
-    extends BaseTest
-{
-    /*
-    /**********************************************************
-    /* Helper classes
-    /**********************************************************
-     */
-
-    final static class CtorValueBean
-        implements JsonSerializable // so we can output as simple String
-    {
-        final String _desc;
-
-        public CtorValueBean(String d) { _desc = d; }
-        public CtorValueBean(int value) { _desc = String.valueOf(value); }
-        public CtorValueBean(long value) { _desc = String.valueOf(value); }
-        public CtorValueBean(double value) { _desc = String.valueOf(value); }
-
-        @Override
-        public void serialize(JsonGenerator jgen, SerializerProvider provider)
-            throws IOException, JsonGenerationException
-        {
-            jgen.writeString(_desc);
-        }
-
-        @Override public String toString() { return _desc; }
-
-        @Override public boolean equals(Object o) {
-            if (!(o instanceof CtorValueBean)) return false;
-            CtorValueBean other = (CtorValueBean) o;
-            return _desc.equals(other._desc);
-        }
-        @Override
-        public void serializeWithType(JsonGenerator jgen,
-                SerializerProvider provider, TypeSerializer typeSer)
-                throws IOException, JsonProcessingException {
-        }
-    }
-
-    final static class FactoryValueBean
-    {
-        final String _desc;
-
-        protected FactoryValueBean(String desc, int dummy) { _desc = desc; }
-
-        public static FactoryValueBean valueOf(String v) { return new FactoryValueBean(v, 0); }
-        public static FactoryValueBean valueOf(int v) { return new FactoryValueBean(String.valueOf(v), 0); }
-        public static FactoryValueBean valueOf(long v) { return new FactoryValueBean(String.valueOf(v), 0); }
-
-        @Override public String toString() { return _desc; }
-    }
-
-    static class OtherBean {
-        SomeIncompatibleType o;
-
-        protected OtherBean(SomeIncompatibleType o) {
-            this.o = o;
-        }
-
-        static class SomeIncompatibleType { }
-    }
-
-    /**
-     * Simple test bean
-     */
-    public final static class TestBean
-    {
-        int _x;
-        long _y;
-        String _desc;
-        URI _uri;
-        Collection<?> _misc;
-
-        // Explicit constructor
-        public TestBean(int x, long y, String desc, URI uri, Collection<?> misc)
-        {
-            _x = x;
-            _y = y;
-            _desc = desc;
-            _uri = uri;
-            _misc = misc;
-        }
-
-        // plus default one that is needed for deserialization
-        public TestBean() { }
-
-        public String getDesc() { return _desc; }
-        public int getX() { return _x; }
-        public long getY() { return _y; }
-        public URI getURI() { return _uri; }
-        public Collection<?> getMisc() { return _misc; }
-
-        public void setDesc(String value) { _desc = value; }
-        public void setX(int value) { _x = value; }
-        public void setY(long value) { _y = value; }
-        public void setURI(URI value) { _uri = value; }
-        public void setMisc(Collection<?> value) { _misc = value; }
-
-        @Override
-        public boolean equals(Object o)
-        {
-            if (o == null || o.getClass() != getClass()) return false;
-            TestBean other = (TestBean) o;
-            return (other._x == _x)
-                && (other._y == _y)
-                && (other._desc.equals(_desc))
-                && (other._uri.equals(_uri))
-                && (other._misc.equals(_misc))
-                ;
-        }
-
-        @Override
-        public String toString()
-        {
-            StringBuilder sb = new StringBuilder();
-            sb.append("[TestBean ");
-            sb.append("x=").append(_x);
-            sb.append(" y=").append(_y);
-            sb.append(" desc=").append(_desc);
-            sb.append(" uri=").append(_uri);
-            sb.append(" misc=").append(_misc);
-            sb.append("]");
-            return sb.toString();
-        }
-    }
-
-    /**
-     * Another test bean, this one containing a typed list. Needed to ensure
-     * that generics type information is properly accessed via mutator methods.
-     * Note: List elements must be something other than what 'untyped' mapper
-     * would produce from serialization.
-     */
-    public final static class BeanWithList
-    {
-        List<CtorValueBean> _beans;
-
-        public BeanWithList() { }
-        public BeanWithList(List<CtorValueBean> beans) { _beans = beans; }
-
-        public List<CtorValueBean> getBeans() { return _beans; }
-
-        public void setBeans(List<CtorValueBean> beans) {
-            _beans = beans;
-        }
-
-        @Override
-        public int hashCode() { return (_beans == null) ? -1 : _beans.size(); }
-
-        @Override
-        public boolean equals(Object o) {
-            if (!(o instanceof BeanWithList)) return false;
-            BeanWithList other = BeanWithList.class.cast(o);
-            return _beans.equals(other._beans);
-        }
-
-        @Override
-            public String toString() {
-            StringBuilder sb = new StringBuilder();
-            sb.append("[Bean, list ");
-            if (_beans == null) {
-                sb.append("NULL");
-            } else {
-                sb.append('(').append(_beans.size()).append('/');
-                sb.append(_beans.getClass().getName()).append(") ");
-                boolean type = false;
-                for (CtorValueBean bean : _beans) {
-                    if (!type) {
-                        sb.append("(").append(bean.getClass().getSimpleName()).append(")");
-                        type = true;
-                    }
-                    sb.append(bean);
-                    sb.append(' ');
-                }
-            }
-            sb.append(']');
-            return sb.toString();
-        }
-    }
-
-    /*
-    /**********************************************************
-    /* Deserialization from simple types (String, int)
-    /**********************************************************
-     */
-
-    private final ObjectMapper MAPPER = new ObjectMapper();
-    
-    public void testFromStringCtor() throws Exception
-    {
-        CtorValueBean result = MAPPER.readValue("\"abc\"", CtorValueBean.class);
-        assertEquals("abc", result.toString());
-    }
-
-    public void testFromIntCtor() throws Exception
-    {
-        CtorValueBean result = MAPPER.readValue("13", CtorValueBean.class);
-        assertEquals("13", result.toString());
-
-        try {
-            OtherBean otherResult = MAPPER.readValue("13", OtherBean.class);
-            fail("Expected an exception, but got result value: "+otherResult.o);
-        } catch (JsonMappingException e) {
-            verifyException(e, "deserialize from Number value",
-                    "no int/Integer-argument constructor/factory method");
-            assertValidLocation(e.getLocation());
-        }
-    }
-
-    public void testFromLongCtor() throws Exception
-    {
-        // Must use something that is forced as Long...
-        long value = 12345678901244L;
-        CtorValueBean result = MAPPER.readValue(""+value, CtorValueBean.class);
-        assertEquals(""+value, result.toString());
-
-        try {
-            OtherBean otherResult = MAPPER.readValue(""+value, OtherBean.class);
-            fail("Expected an exception, but got result value: "+otherResult.o);
-        } catch (JsonMappingException e) {
-            verifyException(e, "deserialize from Number value",
-                    "no long/Long-argument constructor/factory method");
-            assertValidLocation(e.getLocation());
-        }
-    }
-
-    public void testFromDoubleCtor() throws Exception
-    {
-        CtorValueBean result = MAPPER.readValue("13.5", CtorValueBean.class);
-        assertEquals("13.5", result.toString());
-
-        try {
-            OtherBean otherResult = MAPPER.readValue("13.5", OtherBean.class);
-            fail("Expected an exception, but got result value: "+otherResult.o);
-        } catch (JsonMappingException e) {
-            verifyException(e, "deserialize from Number value",
-                    "no double/Double-argument constructor/factory method");
-            assertValidLocation(e.getLocation());
-        }
-    }
-
-    public void testFromStringFactory() throws Exception
-    {
-        FactoryValueBean result = MAPPER.readValue("\"abc\"", FactoryValueBean.class);
-        assertEquals("abc", result.toString());
-    }
-
-    public void testFromIntFactory() throws Exception
-    {
-        FactoryValueBean result = MAPPER.readValue("13", FactoryValueBean.class);
-        assertEquals("13", result.toString());
-    }
-
-    public void testFromLongFactory() throws Exception
-    {
-        // Must use something that is forced as Long...
-        long value = 12345678901244L;
-        FactoryValueBean result = MAPPER.readValue(""+value, FactoryValueBean.class);
-        assertEquals(""+value, result.toString());
-    }
-
-    /*
-    /**********************************************************
-    /* Deserialization from JSON Object
-    /**********************************************************
-     */
-
-    public void testSimpleBean() throws Exception
-    {
-        ArrayList<Object> misc = new ArrayList<Object>();
-        misc.add("xyz");
-        misc.add(42);
-        misc.add(null);
-        misc.add(Boolean.TRUE);
-        TestBean bean = new TestBean(13, -900L, "\"test\"", new URI("http://foobar.com"), misc);
-
-        // Hmmh. We probably should use serializer too... easier
-        String json = MAPPER.writeValueAsString(bean);
-
-        TestBean result = MAPPER.readValue(json, TestBean.class);
-        assertEquals(bean, result);
-    }
-
-    public void testListBean() throws Exception
-    {
-        final int COUNT = 13;
-        ArrayList<CtorValueBean> beans = new ArrayList<CtorValueBean>();
-        for (int i = 0; i < COUNT; ++i) {
-            beans.add(new CtorValueBean(i));
-        }
-        BeanWithList bean = new BeanWithList(beans);
-
-        StringWriter sw = new StringWriter();
-        MAPPER.writeValue(sw, bean);
-
-        BeanWithList result = MAPPER.readValue(sw.toString(), BeanWithList.class);
-        assertEquals(bean, result);
-    }
-
-    /**
-     * Also, let's verify that unknown fields cause an exception with default
-     * settings.
-     */
-    public void testUnknownFields() throws Exception
-    {
-        try {
-            TestBean bean = MAPPER.readValue("{ \"foobar\" : 3 }", TestBean.class);
-            fail("Expected an exception, got bean: "+bean);
-        } catch (JsonMappingException jse) {
-            ;
-        }
-    }
-}
diff --git a/src/test/java/com/fasterxml/jackson/databind/TestObjectMapperBeanSerializer.java b/src/test/java/com/fasterxml/jackson/databind/TestObjectMapperBeanSerializer.java
deleted file mode 100644
index 11deefe..0000000
--- a/src/test/java/com/fasterxml/jackson/databind/TestObjectMapperBeanSerializer.java
+++ /dev/null
@@ -1,231 +0,0 @@
-package com.fasterxml.jackson.databind;
-
-
-import java.io.*;
-import java.net.*;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-
-import com.fasterxml.jackson.core.*;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-/**
- * This unit test suite tries to verify that the "Native" java type
- * mapper can properly serialize Java core objects to JSON.
- *
- * @author Scott Dixon
- */
-public class TestObjectMapperBeanSerializer
-    extends BaseTest
-{
-    /**
-     * Sanity test to ensure the pieces all work when put together.
-     */
-    public void testComplexObject()
-        throws Exception
-    {
-        FixtureObject  aTestObj = new FixtureObject();
-        ObjectMapper aMapper  = new ObjectMapper();
-        StringWriter aWriter = new StringWriter();
-        JsonGenerator aGen = new JsonFactory().createGenerator(aWriter);
-        aMapper.writeValue(aGen, aTestObj);
-        aGen.close();
-        JsonParser jp = new JsonFactory().createParser(new StringReader(aWriter.toString()));
-
-        assertEquals(JsonToken.START_OBJECT, jp.nextToken());
-
-        while (jp.nextToken() != JsonToken.END_OBJECT) {
-            assertEquals(JsonToken.FIELD_NAME, jp.getCurrentToken());
-            String name = jp.getCurrentName();
-            JsonToken t = jp.nextToken();
-
-            if (name.equals("uri") || name.equals("url")) {
-                assertToken(JsonToken.VALUE_STRING, t);
-                assertEquals(FixtureObjectBase.VALUE_URSTR, getAndVerifyText(jp));
-            } else if (name.equals("testNull")) {
-                assertToken(JsonToken.VALUE_NULL, t);
-            } else if (name.equals("testString")) {
-                assertToken(JsonToken.VALUE_STRING, t);
-                assertEquals(FixtureObjectBase.VALUE_STRING, getAndVerifyText(jp));
-            } else if (name.equals("testBoolean")) {
-                assertToken(JsonToken.VALUE_TRUE, t);
-            } else if (name.equals("testEnum")) {
-                assertToken(JsonToken.VALUE_STRING, t);
-                assertEquals(FixtureObjectBase.VALUE_ENUM.toString(),getAndVerifyText(jp));
-            } else if (name.equals("testInteger")) {
-                assertToken(JsonToken.VALUE_NUMBER_INT, t);
-                assertEquals(jp.getIntValue(),FixtureObjectBase.VALUE_INT);
-            } else if (name.equals("testLong")) {
-                assertToken(JsonToken.VALUE_NUMBER_INT, t);
-                assertEquals(jp.getLongValue(),FixtureObjectBase.VALUE_LONG);
-            } else if (name.equals("testBigInteger")) {
-                assertToken(JsonToken.VALUE_NUMBER_INT, t);
-                assertEquals(jp.getLongValue(),FixtureObjectBase.VALUE_BIGINT.longValue());
-            } else if (name.equals("testBigDecimal")) {
-                assertToken(JsonToken.VALUE_NUMBER_FLOAT, t);
-                assertEquals(jp.getText(), FixtureObjectBase.VALUE_BIGDEC.toString());
-            } else if (name.equals("testCharacter")) {
-                assertToken(JsonToken.VALUE_STRING, t);
-                assertEquals(String.valueOf(FixtureObjectBase.VALUE_CHAR), getAndVerifyText(jp));
-            } else if (name.equals("testShort")) {
-                assertToken(JsonToken.VALUE_NUMBER_INT, t);
-                assertEquals(jp.getIntValue(),FixtureObjectBase.VALUE_SHORT);
-            } else if (name.equals("testByte")) {
-                assertToken(JsonToken.VALUE_NUMBER_INT, t);
-                assertEquals(jp.getIntValue(),FixtureObjectBase.VALUE_BYTE);
-            } else if (name.equals("testFloat")) {
-                assertToken(JsonToken.VALUE_NUMBER_FLOAT, t);
-                assertEquals(jp.getDecimalValue().floatValue(),FixtureObjectBase.VALUE_FLOAT);
-            } else if (name.equals("testDouble")) {
-                assertToken(JsonToken.VALUE_NUMBER_FLOAT, t);
-                assertEquals(jp.getDoubleValue(),FixtureObjectBase.VALUE_DBL);
-            } else if (name.equals("testStringBuffer")) {
-                assertToken(JsonToken.VALUE_STRING, t);
-                assertEquals(FixtureObjectBase.VALUE_STRING, getAndVerifyText(jp));
-            } else if (name.equals("testError")) {
-                // More complicated...
-                assertToken(JsonToken.START_OBJECT, t);
-
-                //getTestError->Exception::getCause
-                
-                while (jp.nextToken() == JsonToken.FIELD_NAME) {
-                    name = jp.getCurrentName();
-                    if (name.equals("cause")) {
-                        assertEquals(JsonToken.VALUE_NULL, jp.nextToken());
-                    } else if (name.equals("message")) {
-                        assertEquals(JsonToken.VALUE_STRING, jp.nextToken());
-                        assertEquals(FixtureObjectBase.VALUE_ERRTXT, getAndVerifyText(jp));
-                    } else if (name.equals("localizedMessage")) {
-                        assertEquals(JsonToken.VALUE_STRING, jp.nextToken());
-                    } else if (name.equals("stackTrace")) {
-                        assertEquals(JsonToken.START_ARRAY,jp.nextToken());
-                        int i = 0;
-                        while(jp.nextToken() != JsonToken.END_ARRAY) {
-                            if(i >= 100000) {
-                                assertTrue("Probably run away loop in test. StackTrack Array was not properly closed.",false);
-                            }
-                        }
-                    } else if (name.equals("suppressed")) {
-                        // JDK 7 has introduced a new property 'suppressed' to Throwable; skip if seen
-                        assertEquals(JsonToken.START_ARRAY,jp.nextToken());
-                        assertEquals(JsonToken.END_ARRAY,jp.nextToken());
-                    } else {
-                        fail("Unexpected field name '"+name+"'");
-                    }
-                }
-                //CLOSE OF THE EXCEPTION
-                assertEquals(JsonToken.END_OBJECT, jp.getCurrentToken());
-            } else {
-                fail("Unexpected field, name '"+name+"'");
-            }
-        }
-
-        //END OF TOKEN PARSING
-        assertNull(jp.nextToken());
-        jp.close();
-    }
-
-    private static enum EFixtureEnum
-    {
-        THIS_IS_AN_ENUM_VALUE_0,
-        THIS_IS_AN_ENUM_VALUE_1,
-        THIS_IS_AN_ENUM_VALUE_2,
-        THIS_IS_AN_ENUM_VALUE_3,
-    }
-
-    static class FixtureObjectBase
-    {
-        public static final String       VALUE_STRING = "foobar";
-        public static final EFixtureEnum VALUE_ENUM   = EFixtureEnum.THIS_IS_AN_ENUM_VALUE_2;
-        public static final int          VALUE_INT    = Integer.MIN_VALUE;
-        public static final long         VALUE_LONG   = Long.MIN_VALUE;
-        public static final BigInteger   VALUE_BIGINT = new BigInteger((new Long(Long.MAX_VALUE)).toString());
-        public static final BigDecimal   VALUE_BIGDEC = new BigDecimal((new Double(Double.MAX_VALUE)).toString());
-        // this is not necessarily a good char to check
-        public static final char         VALUE_CHAR   = Character.MAX_VALUE;
-        public static final short        VALUE_SHORT  = Short.MAX_VALUE;
-        public static final byte         VALUE_BYTE   = Byte.MAX_VALUE;
-        public static final float        VALUE_FLOAT  = Float.MAX_VALUE;
-        public static final double       VALUE_DBL    = Double.MAX_VALUE;
-        public static final String       VALUE_ERRTXT = "This is the message text for the test error.";
-
-        public static final String       VALUE_URSTR  = "http://jackson.codehaus.org/hi?var1=foo%20bar";
-
-        public URL getURL() throws IOException
-        {
-            return new URL(VALUE_URSTR);
-        }
-
-        public URI getURI() throws IOException
-        {
-            try {
-                return new URI(VALUE_URSTR);
-            } catch (Exception e) {
-                throw new IllegalArgumentException(e);
-            }
-        }
-        public String getTestNull()
-        {
-            return null;
-        }
-        public String getTestString()
-        {
-            return VALUE_STRING;
-        }
-        public boolean getTestBoolean()
-        {
-            return true;
-        }
-        public EFixtureEnum getTestEnum()
-        {
-            return VALUE_ENUM;
-        }
-        public int getTestInteger()
-        {
-            return VALUE_INT;
-        }
-        public long getTestLong()
-        {
-            return VALUE_LONG;
-        }
-        public BigInteger getTestBigInteger()
-        {
-            return VALUE_BIGINT;
-        }
-        public BigDecimal getTestBigDecimal()
-        {
-            return VALUE_BIGDEC;
-        }
-        public char getTestCharacter()
-        {
-            return VALUE_CHAR;
-        }
-        public short getTestShort()
-        {
-            return VALUE_SHORT;
-        }
-        public byte getTestByte()
-        {
-            return VALUE_BYTE;
-        }
-        public float getTestFloat()
-        {
-            return VALUE_FLOAT;
-        }
-        public double getTestDouble()
-        {
-            return VALUE_DBL;
-        }
-        public StringBuffer getTestStringBuffer()
-        {
-            return new StringBuffer(VALUE_STRING);
-        }
-    }
-
-    static class FixtureObject extends FixtureObjectBase
-    {
-        public Exception getTestError() {
-            return new Exception(VALUE_ERRTXT);
-        }
-    }
-}
diff --git a/src/test/java/com/fasterxml/jackson/databind/TestStdDateFormat.java b/src/test/java/com/fasterxml/jackson/databind/TestStdDateFormat.java
deleted file mode 100644
index adff361..0000000
--- a/src/test/java/com/fasterxml/jackson/databind/TestStdDateFormat.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package com.fasterxml.jackson.databind;
-
-import java.text.ParseException;
-import java.util.*;
-
-import com.fasterxml.jackson.databind.util.StdDateFormat;
-
-public class TestStdDateFormat
-    extends BaseMapTest
-{
-    public void testFactories() {
-        TimeZone tz = TimeZone.getTimeZone("GMT");
-        Locale loc = Locale.US;
-        assertNotNull(StdDateFormat.getISO8601Format(tz, loc));
-        assertNotNull(StdDateFormat.getRFC1123Format(tz, loc));
-    }
-
-    // [databind#803
-    public void testLenient() throws Exception
-    {
-        StdDateFormat f = StdDateFormat.instance;
-
-        // default should be lenient
-        assertTrue(f.isLenient());
-
-        StdDateFormat f2 = f.clone();
-        assertTrue(f2.isLenient());
-
-        f2.setLenient(false);
-        assertFalse(f2.isLenient());
-
-        f2.setLenient(true);
-        assertTrue(f2.isLenient());
-
-        // and for testing, finally, leave as non-lenient
-        f2.setLenient(false);
-        assertFalse(f2.isLenient());
-        StdDateFormat f3 = f2.clone();
-        assertFalse(f3.isLenient());
-
-        // first, legal dates are... legal
-        Date dt = f3.parse("2015-11-30");
-        assertNotNull(dt);
-
-        // but as importantly, when not lenient, do not allow
-        try {
-            f3.parse("2015-11-32");
-            fail("Should not pass");
-        } catch (ParseException e) {
-            verifyException(e, "can not parse date");
-        }
-
-        // ... yet, with lenient, do allow
-        f3.setLenient(true);
-        dt = f3.parse("2015-11-32");
-        assertNotNull(dt);
-    }
-    
-    public void testInvalid() {
-        StdDateFormat std = new StdDateFormat();
-        try {
-            std.parse("foobar");
-        } catch (java.text.ParseException e) {
-            verifyException(e, "Can not parse");
-        }
-    }
-}
diff --git a/src/test/java/com/fasterxml/jackson/databind/access/TestSerAnyGetter.java b/src/test/java/com/fasterxml/jackson/databind/access/TestAnyGetterAccess.java
similarity index 97%
rename from src/test/java/com/fasterxml/jackson/databind/access/TestSerAnyGetter.java
rename to src/test/java/com/fasterxml/jackson/databind/access/TestAnyGetterAccess.java
index 4ca471b..311a86b 100644
--- a/src/test/java/com/fasterxml/jackson/databind/access/TestSerAnyGetter.java
+++ b/src/test/java/com/fasterxml/jackson/databind/access/TestAnyGetterAccess.java
@@ -9,7 +9,7 @@
  * Separate tests located in different package than code being
  * exercised; needed to trigger some access-related failures.
  */
-public class TestSerAnyGetter
+public class TestAnyGetterAccess
     extends BaseMapTest
 {
     /*
diff --git a/src/test/java/com/fasterxml/jackson/databind/cfg/BogusFormatFeature.java b/src/test/java/com/fasterxml/jackson/databind/cfg/BogusFormatFeature.java
new file mode 100644
index 0000000..c059148
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/cfg/BogusFormatFeature.java
@@ -0,0 +1,31 @@
+package com.fasterxml.jackson.databind.cfg;
+
+import com.fasterxml.jackson.core.FormatFeature;
+
+public enum BogusFormatFeature
+    implements FormatFeature
+{
+    FF_ENABLED_BY_DEFAULT(true),
+    FF_DISABLED_BY_DEFAULT(false);
+
+    private boolean _default;
+
+    private BogusFormatFeature(boolean d) {
+        _default = d;
+    }
+
+    @Override
+    public boolean enabledByDefault() {
+        return _default;
+    }
+
+    @Override
+    public int getMask() {
+        return (1 << ordinal());
+    }
+
+    @Override
+    public boolean enabledIn(int flags) {
+        return (flags & getMask()) != 0;
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/cfg/ConfigObjectsTest.java b/src/test/java/com/fasterxml/jackson/databind/cfg/ConfigObjectsTest.java
new file mode 100644
index 0000000..0ace3b0
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/cfg/ConfigObjectsTest.java
@@ -0,0 +1,30 @@
+package com.fasterxml.jackson.databind.cfg;
+
+import com.fasterxml.jackson.databind.*;
+
+import com.fasterxml.jackson.databind.jsontype.SubtypeResolver;
+import com.fasterxml.jackson.databind.jsontype.impl.StdSubtypeResolver;
+
+public class ConfigObjectsTest extends BaseMapTest
+{
+    static class Base { }
+    static class Sub extends Base { }
+
+    public void testSubtypeResolver() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        SubtypeResolver res = mapper.getSubtypeResolver();
+        assertTrue(res instanceof StdSubtypeResolver);
+
+        StdSubtypeResolver repl = new StdSubtypeResolver();
+        repl.registerSubtypes(Sub.class);
+        mapper.setSubtypeResolver(repl);
+        assertSame(repl, mapper.getSubtypeResolver());
+    }
+
+    public void testMics() throws Exception
+    {
+        assertFalse(MapperFeature.AUTO_DETECT_FIELDS.enabledIn(0));
+        assertTrue(MapperFeature.AUTO_DETECT_FIELDS.enabledIn(-1));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/cfg/DatabindContextTest.java b/src/test/java/com/fasterxml/jackson/databind/cfg/DatabindContextTest.java
new file mode 100644
index 0000000..9da87a0
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/cfg/DatabindContextTest.java
@@ -0,0 +1,22 @@
+package com.fasterxml.jackson.databind.cfg;
+
+import com.fasterxml.jackson.databind.*;
+
+public class DatabindContextTest extends BaseMapTest
+{
+    private final ObjectMapper MAPPER = objectMapper();
+
+    public void testDeserializationContext() throws Exception
+    {
+        DeserializationContext ctxt = MAPPER.getDeserializationContext();
+        // should be ok to try to resolve `null`
+        assertNull(ctxt.constructType((Class<?>) null));
+        assertNull(ctxt.constructType((java.lang.reflect.Type) null));
+    }
+
+    public void testSerializationContext() throws Exception
+    {
+        SerializerProvider ctxt = MAPPER.getSerializerProvider();
+        assertNull(ctxt.constructType(null));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/cfg/DeserializationConfigTest.java b/src/test/java/com/fasterxml/jackson/databind/cfg/DeserializationConfigTest.java
new file mode 100644
index 0000000..5a78e89
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/cfg/DeserializationConfigTest.java
@@ -0,0 +1,127 @@
+package com.fasterxml.jackson.databind.cfg;
+
+import java.util.Collections;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.introspect.ClassIntrospector;
+
+public class DeserializationConfigTest extends BaseMapTest
+{
+    private final ObjectMapper MAPPER = new ObjectMapper();
+
+    public void testFeatureDefaults()
+    {
+        ObjectMapper m = new ObjectMapper();
+        DeserializationConfig cfg = m.getDeserializationConfig();
+
+        // Expected defaults:
+        assertTrue(cfg.isEnabled(MapperFeature.USE_ANNOTATIONS));
+        assertTrue(cfg.isEnabled(MapperFeature.AUTO_DETECT_SETTERS));
+        assertTrue(cfg.isEnabled(MapperFeature.AUTO_DETECT_CREATORS));
+        assertTrue(cfg.isEnabled(MapperFeature.USE_GETTERS_AS_SETTERS));
+        assertTrue(cfg.isEnabled(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS));
+
+        assertFalse(cfg.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS));
+        assertFalse(cfg.isEnabled(DeserializationFeature.USE_BIG_INTEGER_FOR_INTS));
+
+        assertTrue(cfg.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));
+    }
+
+    public void testBasicFeatures() throws Exception
+    {
+        DeserializationConfig config = MAPPER.getDeserializationConfig();
+        assertTrue(config.hasDeserializationFeatures(DeserializationFeature.EAGER_DESERIALIZER_FETCH.getMask()));
+        assertFalse(config.hasDeserializationFeatures(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY.getMask()));
+        assertTrue(config.hasSomeOfFeatures(DeserializationFeature.EAGER_DESERIALIZER_FETCH.getMask()
+                + DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY.getMask()));
+        assertFalse(config.hasSomeOfFeatures(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY.getMask()));
+
+        // if no changes then same config object
+        assertSame(config, config.without());
+        assertSame(config, config.with());
+        assertSame(config, config.with(MAPPER.getSubtypeResolver()));
+
+        // and then change
+        DeserializationConfig newConfig = config.with(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
+        assertNotSame(config, newConfig);
+        config = newConfig;
+        
+        // but another attempt with no real change returns same
+        assertSame(config, config.with(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES));
+        assertNotSame(config, config.with(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, false));
+
+        assertNotSame(config, config.with(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT,
+                DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES));
+    }
+
+    public void testParserFeatures() throws Exception
+    {
+        DeserializationConfig config = MAPPER.getDeserializationConfig();
+        assertNotSame(config, config.with(JsonParser.Feature.ALLOW_COMMENTS));
+        assertNotSame(config, config.withFeatures(JsonParser.Feature.ALLOW_COMMENTS,
+                JsonParser.Feature.ALLOW_MISSING_VALUES));
+
+        assertNotSame(config, config.without(JsonParser.Feature.ALLOW_COMMENTS));
+        assertNotSame(config, config.withoutFeatures(JsonParser.Feature.ALLOW_COMMENTS,
+                JsonParser.Feature.ALLOW_MISSING_VALUES));
+    }
+
+    public void testFormatFeatures() throws Exception
+    {
+        DeserializationConfig config = MAPPER.getDeserializationConfig();
+        assertNotSame(config, config.with(BogusFormatFeature.FF_DISABLED_BY_DEFAULT));
+        assertNotSame(config, config.withFeatures(BogusFormatFeature.FF_DISABLED_BY_DEFAULT,
+                BogusFormatFeature.FF_ENABLED_BY_DEFAULT));
+        assertNotSame(config, config.without(BogusFormatFeature.FF_ENABLED_BY_DEFAULT));
+        assertNotSame(config, config.withoutFeatures(BogusFormatFeature.FF_DISABLED_BY_DEFAULT,
+                BogusFormatFeature.FF_ENABLED_BY_DEFAULT));
+    }
+
+    /* Test to verify that we don't overflow number of features; if we
+     * hit the limit, need to change implementation -- this test just
+     * gives low-water mark
+     */
+    public void testEnumIndexes()
+    {
+        int max = 0;
+        
+        for (DeserializationFeature f : DeserializationFeature.values()) {
+            max = Math.max(max, f.ordinal());
+        }
+        if (max >= 31) { // 31 is actually ok; 32 not
+            fail("Max number of DeserializationFeature enums reached: "+max);
+        }
+    }
+
+    public void testOverrideIntrospectors()
+    {
+        ObjectMapper m = new ObjectMapper();
+        DeserializationConfig cfg = m.getDeserializationConfig();
+        // and finally, ensure we could override introspectors
+        cfg = cfg.with((ClassIntrospector) null); // no way to verify tho
+        cfg = cfg.with((AnnotationIntrospector) null);
+        assertNull(cfg.getAnnotationIntrospector());
+    }
+
+    public void testMisc() throws Exception
+    {
+        DeserializationConfig config = MAPPER.getDeserializationConfig();
+        assertEquals(JsonInclude.Value.empty(), config.getDefaultPropertyInclusion());
+        assertEquals(JsonInclude.Value.empty(), config.getDefaultPropertyInclusion(String.class));
+
+        assertSame(config, config.withRootName((PropertyName) null)); // defaults to 'none'
+
+        DeserializationConfig newConfig = config.withRootName(PropertyName.construct("foobar"));
+        assertNotSame(config, newConfig);
+        config = newConfig;
+        assertSame(config, config.withRootName(PropertyName.construct("foobar")));
+
+        assertSame(config, config.with(config.getAttributes()));
+        assertNotSame(config, config.with(new ContextAttributes.Impl(Collections.singletonMap("a", "b"))));
+
+        // should also be able to introspect:
+        assertNotNull(config.introspectDirectClassAnnotations(getClass()));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/cfg/SerConfigTest.java b/src/test/java/com/fasterxml/jackson/databind/cfg/SerConfigTest.java
new file mode 100644
index 0000000..adb3b18
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/cfg/SerConfigTest.java
@@ -0,0 +1,77 @@
+package com.fasterxml.jackson.databind.cfg;
+
+import java.util.Collections;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonGenerator;
+
+import com.fasterxml.jackson.databind.*;
+
+public class SerConfigTest extends BaseMapTest
+{
+    private final ObjectMapper MAPPER = new ObjectMapper();
+
+    public void testSerConfig() throws Exception
+    {
+        SerializationConfig config = MAPPER.getSerializationConfig();
+        assertTrue(config.hasSerializationFeatures(SerializationFeature.FAIL_ON_EMPTY_BEANS.getMask()));
+        assertFalse(config.hasSerializationFeatures(SerializationFeature.CLOSE_CLOSEABLE.getMask()));
+        assertEquals(JsonInclude.Value.empty(), config.getDefaultPropertyInclusion());
+        assertEquals(JsonInclude.Value.empty(), config.getDefaultPropertyInclusion(String.class));
+        assertFalse(config.useRootWrapping());
+
+        // if no changes then same config object
+        assertSame(config, config.without());
+        assertSame(config, config.with());
+        assertSame(config, config.with(MAPPER.getSubtypeResolver()));
+
+        // and then change
+        SerializationConfig newConfig = config.with(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
+        assertNotSame(config, newConfig);
+        config = newConfig;
+        assertSame(config, config.with(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES));
+        assertNotSame(config, config.with(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, false));
+
+        assertNotSame(config, config.with(SerializationFeature.INDENT_OUTPUT,
+                SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS));
+        
+        assertSame(config, config.withRootName((PropertyName) null)); // defaults to 'none'
+
+        newConfig = config.withRootName(PropertyName.construct("foobar"));
+        assertNotSame(config, newConfig);
+        assertTrue(newConfig.useRootWrapping());
+
+        assertSame(config, config.with(config.getAttributes()));
+        assertNotSame(config, config.with(new ContextAttributes.Impl(Collections.singletonMap("a", "b"))));
+
+        assertNotNull(config.introspectDirectClassAnnotations(getClass()));
+    }
+
+    public void testGeneratorFeatures() throws Exception
+    {
+        SerializationConfig config = MAPPER.getSerializationConfig();
+        JsonFactory f = MAPPER.getFactory();
+        assertFalse(config.isEnabled(JsonGenerator.Feature.ESCAPE_NON_ASCII, f));
+        assertNotSame(config, config.with(JsonGenerator.Feature.ESCAPE_NON_ASCII));
+        SerializationConfig newConfig = config.withFeatures(JsonGenerator.Feature.ESCAPE_NON_ASCII,
+                JsonGenerator.Feature.IGNORE_UNKNOWN);
+        assertNotSame(config, newConfig);
+        assertTrue(newConfig.isEnabled(JsonGenerator.Feature.ESCAPE_NON_ASCII, f));
+
+        assertNotSame(config, config.without(JsonGenerator.Feature.ESCAPE_NON_ASCII));
+        assertNotSame(config, config.withoutFeatures(JsonGenerator.Feature.ESCAPE_NON_ASCII,
+                JsonGenerator.Feature.IGNORE_UNKNOWN));
+    }
+
+    public void testFormatFeatures() throws Exception
+    {
+        SerializationConfig config = MAPPER.getSerializationConfig();
+        assertNotSame(config, config.with(BogusFormatFeature.FF_DISABLED_BY_DEFAULT));
+        assertNotSame(config, config.withFeatures(BogusFormatFeature.FF_DISABLED_BY_DEFAULT,
+                BogusFormatFeature.FF_ENABLED_BY_DEFAULT));
+        assertNotSame(config, config.without(BogusFormatFeature.FF_ENABLED_BY_DEFAULT));
+        assertNotSame(config, config.withoutFeatures(BogusFormatFeature.FF_DISABLED_BY_DEFAULT,
+                BogusFormatFeature.FF_ENABLED_BY_DEFAULT));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/convert/NumericConversionTest.java b/src/test/java/com/fasterxml/jackson/databind/convert/NumericConversionTest.java
index 8055146..287ad4b 100644
--- a/src/test/java/com/fasterxml/jackson/databind/convert/NumericConversionTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/convert/NumericConversionTest.java
@@ -1,6 +1,7 @@
 package com.fasterxml.jackson.databind.convert;
 
 import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.exc.MismatchedInputException;
 
 public class NumericConversionTest extends BaseMapTest
 {
@@ -14,26 +15,32 @@
         assertEquals(1, I.intValue());
         IntWrapper w = MAPPER.readValue("{\"i\":-2.25 }", IntWrapper.class);
         assertEquals(-2, w.i);
+        int[] arr = MAPPER.readValue("[ 1.25 ]", int[].class);
+        assertEquals(1, arr[0]);
 
         try {
             R.forType(Integer.class).readValue("1.5");
             fail("Should not pass");
         } catch (JsonMappingException e) {
-            verifyException(e, "Can not coerce a floating-point");
+            verifyException(e, "Cannot coerce a floating-point");
         }
-
         try {
             R.forType(Integer.TYPE).readValue("1.5");
             fail("Should not pass");
         } catch (JsonMappingException e) {
-            verifyException(e, "Can not coerce a floating-point");
+            verifyException(e, "Cannot coerce a floating-point");
         }
-        
         try {
             R.forType(IntWrapper.class).readValue("{\"i\":-2.25 }");
             fail("Should not pass");
         } catch (JsonMappingException e) {
-            verifyException(e, "Can not coerce a floating-point");
+            verifyException(e, "Cannot coerce a floating-point");
+        }
+        try {
+            R.forType(int[].class).readValue("[ 2.5 ]");
+            fail("Should not pass");
+        } catch (JsonMappingException e) {
+            verifyException(e, "Cannot coerce a floating-point");
         }
     }
 
@@ -44,26 +51,34 @@
         assertEquals(3L, L.longValue());
         LongWrapper w = MAPPER.readValue("{\"l\":-2.25 }", LongWrapper.class);
         assertEquals(-2L, w.l);
+        long[] arr = MAPPER.readValue("[ 1.25 ]", long[].class);
+        assertEquals(1, arr[0]);
 
         try {
             R.forType(Long.class).readValue("1.5");
             fail("Should not pass");
-        } catch (JsonMappingException e) {
-            verifyException(e, "Can not coerce a floating-point");
+        } catch (MismatchedInputException e) {
+            verifyException(e, "Cannot coerce a floating-point");
         }
 
         try {
             R.forType(Long.TYPE).readValue("1.5");
             fail("Should not pass");
-        } catch (JsonMappingException e) {
-            verifyException(e, "Can not coerce a floating-point");
+        } catch (MismatchedInputException e) {
+            verifyException(e, "Cannot coerce a floating-point");
         }
         
         try {
             R.forType(LongWrapper.class).readValue("{\"l\": 7.7 }");
             fail("Should not pass");
-        } catch (JsonMappingException e) {
-            verifyException(e, "Can not coerce a floating-point");
+        } catch (MismatchedInputException e) {
+            verifyException(e, "Cannot coerce a floating-point");
+        }
+        try {
+            R.forType(long[].class).readValue("[ 2.5 ]");
+            fail("Should not pass");
+        } catch (MismatchedInputException e) {
+            verifyException(e, "Cannot coerce a floating-point");
         }
     }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/convert/ScalarConversionTest.java b/src/test/java/com/fasterxml/jackson/databind/convert/ScalarConversionTest.java
new file mode 100644
index 0000000..0157f97
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/convert/ScalarConversionTest.java
@@ -0,0 +1,34 @@
+package com.fasterxml.jackson.databind.convert;
+
+import com.fasterxml.jackson.databind.*;
+
+public class ScalarConversionTest extends BaseMapTest
+{
+    private final ObjectMapper MAPPER = new ObjectMapper();
+    
+    // [databind#1433]
+    public void testConvertValueNullPrimitive() throws Exception
+    {
+        assertEquals(Byte.valueOf((byte) 0), MAPPER.convertValue(null, Byte.TYPE));
+        assertEquals(Short.valueOf((short) 0), MAPPER.convertValue(null, Short.TYPE));
+        assertEquals(Integer.valueOf(0), MAPPER.convertValue(null, Integer.TYPE));
+        assertEquals(Long.valueOf(0L), MAPPER.convertValue(null, Long.TYPE));
+        assertEquals(Float.valueOf(0f), MAPPER.convertValue(null, Float.TYPE));
+        assertEquals(Double.valueOf(0d), MAPPER.convertValue(null, Double.TYPE));
+        assertEquals(Character.valueOf('\0'), MAPPER.convertValue(null, Character.TYPE));
+        assertEquals(Boolean.FALSE, MAPPER.convertValue(null, Boolean.TYPE));
+    }
+    
+    // [databind#1433]
+    public void testConvertValueNullBoxed() throws Exception
+    {
+        assertNull(MAPPER.convertValue(null, Byte.class));
+        assertNull(MAPPER.convertValue(null, Short.class));
+        assertNull(MAPPER.convertValue(null, Integer.class));
+        assertNull(MAPPER.convertValue(null, Long.class));
+        assertNull(MAPPER.convertValue(null, Float.class));
+        assertNull(MAPPER.convertValue(null, Double.class));
+        assertNull(MAPPER.convertValue(null, Character.class));
+        assertNull(MAPPER.convertValue(null, Boolean.class));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/convert/TestArrayConversions.java b/src/test/java/com/fasterxml/jackson/databind/convert/TestArrayConversions.java
index a4156eb..fbc4751 100644
--- a/src/test/java/com/fasterxml/jackson/databind/convert/TestArrayConversions.java
+++ b/src/test/java/com/fasterxml/jackson/databind/convert/TestArrayConversions.java
@@ -14,16 +14,19 @@
     final static String OVERFLOW_MSG_BYTE = "out of range of Java byte";
     final static String OVERFLOW_MSG = "overflow";
 
-    final ObjectMapper mapper = new ObjectMapper();
+    final static String OVERFLOW_MSG_INT = "out of range of int";
+    final static String OVERFLOW_MSG_LONG = "out of range of long";
+
+    final ObjectMapper MAPPER = new ObjectMapper();
 
     public void testNullXform() throws Exception
     {
         /* when given null, null should be returned without conversion
          * (Java null has no type)
          */
-        assertNull(mapper.convertValue(null, Integer.class));
-        assertNull(mapper.convertValue(null, String.class));
-        assertNull(mapper.convertValue(null, byte[].class));
+        assertNull(MAPPER.convertValue(null, Integer.class));
+        assertNull(MAPPER.convertValue(null, String.class));
+        assertNull(MAPPER.convertValue(null, byte[].class));
     }
 
     /**
@@ -72,7 +75,7 @@
 
         List<Number> expNums = _numberList(data, data.length);
         // Alas, due to type erasure, need to use TypeRef, not just class
-        List<Integer> actNums = mapper.convertValue(data, new TypeReference<List<Integer>>() {});
+        List<Integer> actNums = MAPPER.convertValue(data, new TypeReference<List<Integer>>() {});
         assertEquals(expNums, actNums);
     }
 
@@ -84,7 +87,7 @@
         verifyLongArrayConversion(data, int[].class);
  
         List<Number> expNums = _numberList(data, data.length);
-        List<Long> actNums = mapper.convertValue(data, new TypeReference<List<Long>>() {});
+        List<Long> actNums = MAPPER.convertValue(data, new TypeReference<List<Long>>() {});
         assertEquals(expNums, actNums);        
     }
 
@@ -92,21 +95,21 @@
     {
         // Byte overflow
         try {
-            mapper.convertValue(new int[] { 1000 }, byte[].class);
+            MAPPER.convertValue(new int[] { 1000 }, byte[].class);
         } catch (IllegalArgumentException e) {
             verifyException(e, OVERFLOW_MSG_BYTE);
         }
         // Short overflow
         try {
-            mapper.convertValue(new int[] { -99999 }, short[].class);
+            MAPPER.convertValue(new int[] { -99999 }, short[].class);
         } catch (IllegalArgumentException e) {
             verifyException(e, OVERFLOW_MSG);
         }
         // Int overflow
         try {
-            mapper.convertValue(new long[] { Long.MAX_VALUE }, int[].class);
+            MAPPER.convertValue(new long[] { Long.MAX_VALUE }, int[].class);
         } catch (IllegalArgumentException e) {
-            verifyException(e, OVERFLOW_MSG);
+            verifyException(e, OVERFLOW_MSG_INT);
         }
         // Longs need help of BigInteger...
         BigInteger biggie = BigInteger.valueOf(Long.MAX_VALUE);
@@ -114,13 +117,12 @@
         List<BigInteger> l = new ArrayList<BigInteger>();
         l.add(biggie);
         try {
-            mapper.convertValue(l, int[].class);
+            MAPPER.convertValue(l, long[].class);
         } catch (IllegalArgumentException e) {
-            verifyException(e, OVERFLOW_MSG);
+            verifyException(e, OVERFLOW_MSG_LONG);
         }
-        
     }
-    
+
     /*
     /********************************************************
     /* Helper methods
@@ -171,7 +173,7 @@
         // must be a primitive array, like "int[].class"
         if (!outputType.isArray()) throw new IllegalArgumentException();
         if (!outputType.getComponentType().isPrimitive()) throw new IllegalArgumentException();
-        T result = mapper.convertValue(input, outputType);
+        T result = MAPPER.convertValue(input, outputType);
         // sanity check first:
         assertNotNull(result);
         assertEquals(outputType, result.getClass());
diff --git a/src/test/java/com/fasterxml/jackson/databind/convert/TestBeanConversions.java b/src/test/java/com/fasterxml/jackson/databind/convert/TestBeanConversions.java
index 34c161e..ac08d94 100644
--- a/src/test/java/com/fasterxml/jackson/databind/convert/TestBeanConversions.java
+++ b/src/test/java/com/fasterxml/jackson/databind/convert/TestBeanConversions.java
@@ -3,18 +3,22 @@
 import java.util.LinkedHashMap;
 import java.util.Map;
 
+import com.fasterxml.jackson.core.JsonParser;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.core.TreeNode;
 import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.fasterxml.jackson.databind.util.StdConverter;
 
+/**
+ * Tests for various conversions, especially ones using
+ * {@link ObjectMapper#convertValue(Object, Class)}.
+ */
 public class TestBeanConversions
     extends com.fasterxml.jackson.databind.BaseMapTest
 {
-    final ObjectMapper MAPPER = new ObjectMapper();
-
     static class PointZ {
         public int x, y;
 
@@ -64,7 +68,7 @@
         public Leaf(int v) { value = v; }
     }
     
-    // [Issue#288]
+    // [databind#288]
 
     @JsonSerialize(converter = ConvertingBeanConverter.class)
     static class ConvertingBean { 
@@ -90,6 +94,23 @@
           return new DummyBean(cb.x, cb.y);
        }
     }
+    
+    @JsonDeserialize(using = NullBeanDeserializer.class)
+    static class NullBean {
+        public static final NullBean NULL_INSTANCE = new NullBean();
+    }
+    
+    static class NullBeanDeserializer extends JsonDeserializer<NullBean> {
+        @Override
+        public NullBean getNullValue(final DeserializationContext context) {
+            return NullBean.NULL_INSTANCE;
+        }
+        
+        @Override
+        public NullBean deserialize(final JsonParser parser, final DeserializationContext context) {
+            throw new UnsupportedOperationException();
+        }
+    }
 
     /*
     /**********************************************************
@@ -97,6 +118,8 @@
     /**********************************************************
      */
     
+    private final ObjectMapper MAPPER = new ObjectMapper();
+
     public void testBeanConvert()
     {
         // should have no problems convert between compatible beans...
@@ -124,7 +147,7 @@
         try {
             MAPPER.readValue("{\"boolProp\":\"foobar\"}", BooleanBean.class);
         } catch (JsonMappingException e) {
-            verifyException(e, "Can not deserialize value of type boolean from String");
+            verifyException(e, "Cannot deserialize value of type `boolean` from String");
         }
     }
 
@@ -252,5 +275,15 @@
         String json = MAPPER.writeValueAsString(new ConvertingBean(1, 2));
         // must be  {"a":2,"b":4}
         assertEquals("{\"a\":2,\"b\":4}", json);
-     }
+    }
+    
+    // Test null conversions from [databind#1433]
+    public void testConversionIssue1433() throws Exception
+    {
+        assertNull(MAPPER.convertValue(null, Object.class));
+        assertNull(MAPPER.convertValue(null, PointZ.class));
+        
+        assertSame(NullBean.NULL_INSTANCE,
+                MAPPER.convertValue(null, NullBean.class));
+    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/convert/TestConvertingDeserializer.java b/src/test/java/com/fasterxml/jackson/databind/convert/TestConvertingDeserializer.java
index d88e397..5db3bdf 100644
--- a/src/test/java/com/fasterxml/jackson/databind/convert/TestConvertingDeserializer.java
+++ b/src/test/java/com/fasterxml/jackson/databind/convert/TestConvertingDeserializer.java
@@ -114,7 +114,7 @@
         @JsonDeserialize(converter=ToNumberConverter.class)
         public Number value;
     }
-    
+
     /*
     /**********************************************************
     /* Test methods
diff --git a/src/test/java/com/fasterxml/jackson/databind/convert/TestConvertingSerializer.java b/src/test/java/com/fasterxml/jackson/databind/convert/TestConvertingSerializer.java
index 938b67b..89b9e2f 100644
--- a/src/test/java/com/fasterxml/jackson/databind/convert/TestConvertingSerializer.java
+++ b/src/test/java/com/fasterxml/jackson/databind/convert/TestConvertingSerializer.java
@@ -95,7 +95,22 @@
         }
     }
 
-    // [Issue#359]
+    // [databind#357]
+    static class Value { }
+
+    static class ListWrapper {
+        @JsonSerialize(contentConverter = ValueToStringListConverter.class)
+        public List<Value> list = Arrays.asList(new Value());
+    }
+
+    static class ValueToStringListConverter extends StdConverter<Value, List<String>> {
+        @Override
+        public List<String> convert(Value value) {
+            return Arrays.asList("Hello world!");
+        }
+    }
+
+    // [databind#359]
     static class Bean359 {
         @JsonSerialize(as = List.class, contentAs = Source.class)
         public List<Source> stuff = Arrays.asList(new Source());
@@ -124,7 +139,7 @@
         }
     }
 
-    // [Issue#731]
+    // [databind#731]
     public static class DummyBean {
         public final int a, b;
         public DummyBean(int v1, int v2) {
@@ -150,21 +165,6 @@
         }
     }
 
-    // [databind#357]
-    static class Value { }
-
-    static class ListWrapper {
-        @JsonSerialize(contentConverter = ValueToStringListConverter.class)
-        public List<Value> list = Arrays.asList(new Value());
-    }
-
-    static class ValueToStringListConverter extends StdConverter<Value, List<String>> {
-        @Override
-        public List<String> convert(Value value) {
-            return Arrays.asList("Hello world!");
-        }
-    }
-
     /*
     /**********************************************************
     /* Test methods
@@ -205,6 +205,12 @@
         assertEquals("{\"values\":{\"a\":[1,2]}}", json);
     }
 
+    // [databind#357]
+    public void testConverterForList357() throws Exception {
+        String json = objectWriter().writeValueAsString(new ListWrapper());
+        assertEquals("{\"list\":[[\"Hello world!\"]]}", json);
+    }
+    
     // [databind#359]
     public void testIssue359() throws Exception {
         String json = objectWriter().writeValueAsString(new Bean359());
diff --git a/src/test/java/com/fasterxml/jackson/databind/convert/TestUpdateValue.java b/src/test/java/com/fasterxml/jackson/databind/convert/TestUpdateViaObjectReader.java
similarity index 73%
rename from src/test/java/com/fasterxml/jackson/databind/convert/TestUpdateValue.java
rename to src/test/java/com/fasterxml/jackson/databind/convert/TestUpdateViaObjectReader.java
index ef86c5f..1a09c09 100644
--- a/src/test/java/com/fasterxml/jackson/databind/convert/TestUpdateValue.java
+++ b/src/test/java/com/fasterxml/jackson/databind/convert/TestUpdateViaObjectReader.java
@@ -3,9 +3,11 @@
 import java.io.IOException;
 import java.util.*;
 
-import com.fasterxml.jackson.annotation.JsonView;
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
 import com.fasterxml.jackson.core.*;
 import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
 import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
 import com.fasterxml.jackson.databind.module.SimpleModule;
 
@@ -15,14 +17,9 @@
  * Unit tests for verifying that "updating reader" works as
  * expected.
  */
-public class TestUpdateValue extends BaseMapTest
+@SuppressWarnings("serial")
+public class TestUpdateViaObjectReader extends BaseMapTest
 {
-    /*
-    /********************************************************
-    /* Helper types
-    /********************************************************
-     */
-
     static class Bean {
         public String a = "a";
         public String b = "b";
@@ -36,7 +33,6 @@
         public int x, y;
     }
 
-    // [JACKSON-824]
     public class TextView {}
     public class NumView {}
 
@@ -70,9 +66,9 @@
         @Override
         public DataA deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
             if (p.getCurrentToken() != JsonToken.START_OBJECT) {
-                ctxt.reportWrongTokenException(p, JsonToken.START_OBJECT,
-                        "Wrong current token, expected START_OBJECT, got: %s",
-                        p.getCurrentToken());
+                ctxt.reportInputMismatch(DataA.class,
+                        "Wrong current token, expected START_OBJECT, got: "
+                        +p.getCurrentToken());
                 // never gets here
             }
             /*JsonNode node =*/ p.readValueAsTree();
@@ -82,10 +78,46 @@
             return da;
         }
     }
-    
+
+    // [databind#1831]
+    @JsonTypeInfo(use = Id.NAME)
+    @JsonSubTypes({  @JsonSubTypes.Type(value = Cat.class) })
+    static abstract public class AbstractAnimal { }
+
+    @JsonDeserialize(using = AnimalWrapperDeserializer.class)
+    static class AnimalWrapper {
+        @JsonUnwrapped
+        protected AbstractAnimal animal;
+
+        public void setAnimal(AbstractAnimal animal) {
+            this.animal = animal;
+        }
+    }
+
+    static class Cat extends AbstractAnimal { }
+
+    static class AnimalWrapperDeserializer extends StdDeserializer<AnimalWrapper> {
+        public AnimalWrapperDeserializer() {
+            super(AnimalWrapper.class);
+        }
+
+        @Override
+        public AnimalWrapper deserialize(JsonParser json, DeserializationContext context) throws IOException {
+            AnimalWrapper msg = new AnimalWrapper();
+            msg.setAnimal(json.readValueAs(AbstractAnimal.class));
+            return msg;
+        }
+
+        @Override
+        public AnimalWrapper deserialize(JsonParser json, DeserializationContext context, AnimalWrapper intoValue) throws IOException {
+            intoValue.setAnimal(json.readValueAs(AbstractAnimal.class));
+            return intoValue;
+        }
+    }
+
     /*
     /********************************************************
-    /* Unit tests
+    /* Test methods
     /********************************************************
      */
 
@@ -225,4 +257,21 @@
         assertEquals(5, dbUpdViaNode.da.i);
         assertEquals(13, dbUpdViaNode.k);
     }
+
+    // [databind#1831]
+    public void test1831UsingNode() throws IOException {
+        String catJson = MAPPER.writeValueAsString(new Cat());
+        JsonNode jsonNode = MAPPER.readTree(catJson);
+        AnimalWrapper optionalCat = new AnimalWrapper();
+        ObjectReader r = MAPPER.readerForUpdating(optionalCat);
+        AnimalWrapper result = r.readValue(jsonNode);
+        assertSame(optionalCat, result);
+    }
+
+    public void test1831UsingString() throws IOException {
+        String catJson = MAPPER.writeValueAsString(new Cat());
+        AnimalWrapper optionalCat = new AnimalWrapper();
+        AnimalWrapper result = MAPPER.readerForUpdating(optionalCat).readValue(catJson);
+        assertSame(optionalCat, result);
+    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/convert/UpdateValueTest.java b/src/test/java/com/fasterxml/jackson/databind/convert/UpdateValueTest.java
new file mode 100644
index 0000000..99db96c
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/convert/UpdateValueTest.java
@@ -0,0 +1,101 @@
+package com.fasterxml.jackson.databind.convert;
+
+import java.util.*;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Tests for {@link ObjectMapper#updateValue}.
+ *
+ * @since 2.9
+ */
+public class UpdateValueTest extends BaseMapTest
+{
+    /*
+    /********************************************************
+    /* Test methods; simple containers
+    /********************************************************
+     */
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+
+    public void testMapUpdate() throws Exception
+    {
+        Map<String,Object> base = new LinkedHashMap<>();
+        base.put("a", 345);
+        Map<String,Object> overrides = new LinkedHashMap<>();
+        overrides.put("xyz", Boolean.TRUE);
+        overrides.put("foo", "bar");
+        
+        Map<String,Object> ob = MAPPER.updateValue(base, overrides);
+        // first: should return first argument
+        assertSame(base, ob);
+        assertEquals(3, ob.size());
+        assertEquals(Integer.valueOf(345), ob.get("a"));
+        assertEquals("bar", ob.get("foo"));
+        assertEquals(Boolean.TRUE, ob.get("xyz"));
+    }
+
+    public void testListUpdate() throws Exception
+    {
+        List<Object> base = new ArrayList<>();
+        base.add(123456);
+        base.add(Boolean.FALSE);
+        Object[] overrides = new Object[] { Boolean.TRUE, "zoink!" };
+
+        List<Object> ob = MAPPER.updateValue(base, overrides);
+        // first: should return first argument
+        assertSame(base, ob);
+        assertEquals(4, ob.size());
+        assertEquals(Integer.valueOf(123456), ob.get(0));
+        assertEquals(Boolean.FALSE, ob.get(1));
+        assertEquals(overrides[0], ob.get(2));
+        assertEquals(overrides[1], ob.get(3));
+    }
+
+    public void testArrayUpdate() throws Exception
+    {
+        // Since Arrays are immutable, not sure what "right answer" ought to be
+        Object[] base = new Object[] { Boolean.FALSE, Integer.valueOf(3) };
+        Object[] overrides = new Object[] { Boolean.TRUE, "zoink!" };
+
+        Object[] ob = MAPPER.updateValue(base, overrides);
+        assertEquals(4, ob.length);
+        assertEquals(base[0], ob[0]);
+        assertEquals(base[1], ob[1]);
+        assertEquals(overrides[0], ob[2]);
+        assertEquals(overrides[1], ob[3]);
+    }
+
+    /*
+    /********************************************************
+    /* Test methods; POJOs
+    /********************************************************
+     */
+
+    public void testPOJO() throws Exception
+    {
+        Point base = new Point(42, 28);
+        Map<String,Object> overrides = new LinkedHashMap<>();
+        overrides.put("y", 1234);
+        Point result = MAPPER.updateValue(base, overrides);
+        assertSame(base, result);
+        assertEquals(42, result.x);
+        assertEquals(1234, result.y);
+    }
+
+    /*
+    /********************************************************
+    /* Test methods; other
+    /********************************************************
+     */
+
+    public void testMisc() throws Exception
+    {
+        // if either is `null`, should return first arg
+        assertNull(MAPPER.updateValue(null, "foo"));
+        List<String> input = new ArrayList<>();
+        assertSame(input, MAPPER.updateValue(input, null));
+    }
+
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/Creator1476Test.java b/src/test/java/com/fasterxml/jackson/databind/creators/Creator1476Test.java
deleted file mode 100644
index 94a1eee..0000000
--- a/src/test/java/com/fasterxml/jackson/databind/creators/Creator1476Test.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.fasterxml.jackson.databind.creators;
-
-import com.fasterxml.jackson.annotation.*;
-
-import com.fasterxml.jackson.databind.*;
-
-public class Creator1476Test extends BaseMapTest
-{
-    static final class SimplePojo {
-        private final int intField;
-        private final String stringField;
-
-        public SimplePojo(@JsonProperty("intField") int intField) {
-          this(intField, "empty");
-        }
-
-        public SimplePojo(@JsonProperty("stringField") String stringField) {
-          this(-1, stringField);
-        }
-
-        @JsonCreator
-        public SimplePojo(@JsonProperty("intField") int intField, @JsonProperty("stringField") String stringField) {
-          this.intField = intField;
-          this.stringField = stringField;
-        }
-
-        public int getIntField() {
-          return intField;
-        }
-
-        public String getStringField() {
-          return stringField;
-        }
-    }
-
-    public void testConstructorChoice() throws Exception {
-        ObjectMapper mapper = new ObjectMapper();
-        SimplePojo pojo = mapper.readValue("{ \"intField\": 1, \"stringField\": \"foo\" }", SimplePojo.class);
-
-        assertEquals(1, pojo.getIntField());
-        assertEquals("foo", pojo.getStringField());
-    }
-}
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreators421.java b/src/test/java/com/fasterxml/jackson/databind/creators/TestCreators421.java
deleted file mode 100644
index 27de087..0000000
--- a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreators421.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package com.fasterxml.jackson.databind.creators;
-
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.databind.*;
-import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
-import com.fasterxml.jackson.databind.introspect.AnnotatedParameter;
-import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
-
-public class TestCreators421 extends BaseMapTest
-{
-    static class MultiCtor
-    {
-        protected String _a, _b;
-        
-        private MultiCtor() { }
-        private MultiCtor(String a, String b, Boolean c) {
-            if (c == null) {
-                throw new RuntimeException("Wrong factory!");
-            }
-            _a = a;
-            _b = b;
-        }
-
-        @JsonCreator
-        static MultiCtor factory(@JsonProperty("a") String a, @JsonProperty("b") String b) {
-            return new MultiCtor(a, b, Boolean.TRUE);
-        }
-    }
-
-    @SuppressWarnings("serial")
-    static class MyParamIntrospector extends JacksonAnnotationIntrospector
-    {
-        @Override
-        public String findImplicitPropertyName(AnnotatedMember param) {
-            if (param instanceof AnnotatedParameter) {
-                AnnotatedParameter ap = (AnnotatedParameter) param;
-                switch (ap.getIndex()) {
-                case 0: return "a";
-                case 1: return "b";
-                case 2: return "c";
-                default:
-                    return "param"+ap.getIndex();
-                }
-            }
-            return super.findImplicitPropertyName(param);
-        }
-    }
-    
-    /*
-    /**********************************************************
-    /* Test methods
-    /**********************************************************
-     */
-
-    // [Issue#421]
-    public void testMultiCtor421() throws Exception
-    {
-        final ObjectMapper mapper = new ObjectMapper();
-        mapper.setAnnotationIntrospector(new MyParamIntrospector());
-
-        MultiCtor bean = mapper.readValue(aposToQuotes("{'a':'123','b':'foo'}"), MultiCtor.class);
-        assertNotNull(bean);
-        assertEquals("123", bean._a);
-        assertEquals("foo", bean._b);
-    }
-}
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreators541.java b/src/test/java/com/fasterxml/jackson/databind/creators/TestCreators541.java
deleted file mode 100644
index e0f1214..0000000
--- a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreators541.java
+++ /dev/null
@@ -1,109 +0,0 @@
-package com.fasterxml.jackson.databind.creators;
-
-import java.util.*;
-
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-import com.fasterxml.jackson.databind.*;
-
-public class TestCreators541 extends BaseMapTest
-{
-    static final class Foo {
-
-        @JsonProperty("foo")
-        protected Map<Integer, Bar> foo;
-        @JsonProperty("anumber")
-        protected long anumber;
-
-        public Foo() {
-            anumber = 0;
-        }
-
-        public Map<Integer, Bar> getFoo() {
-            return foo;
-        }
-
-        public long getAnumber() {
-            return anumber;
-        }
-    }
-
-    static final class Bar {
-
-        private final long p;
-        private final List<String> stuff;
-
-        @JsonCreator
-        public Bar(@JsonProperty("p") long p, @JsonProperty("stuff") List<String> stuff) {
-            this.p = p;
-            this.stuff = stuff;
-        }
-
-        @JsonProperty("s")
-        public List<String> getStuff() {
-            return stuff;
-        }
-
-        @JsonProperty("stuff")
-        private List<String> getStuffDeprecated() {
-            return stuff;
-        }
-
-        public long getP() {
-            return p;
-        }
-    }    
-    /*
-    /**********************************************************
-    /* Test methods
-    /**********************************************************
-     */
-
-    public void testCreator541() throws Exception
-    {
-        ObjectMapper mapper = new ObjectMapper();
-
-        mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
-        mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
-        mapper.disable(
-                MapperFeature.AUTO_DETECT_CREATORS,
-                MapperFeature.AUTO_DETECT_FIELDS,
-                MapperFeature.AUTO_DETECT_GETTERS,
-                MapperFeature.AUTO_DETECT_IS_GETTERS,
-                MapperFeature.AUTO_DETECT_SETTERS,
-                MapperFeature.USE_GETTERS_AS_SETTERS
-        );
-        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);  
-
-        final String JSON = "{\n"
-                + "    \"foo\": {\n"
-                + "        \"0\": {\n"
-                + "            \"p\": 0,\n"
-                + "            \"stuff\": [\n"
-                + "              \"a\", \"b\" \n"
-                + "            ]   \n"
-                + "        },\n"
-                + "        \"1\": {\n"
-                + "            \"p\": 1000,\n"
-                + "            \"stuff\": [\n"
-                + "              \"c\", \"d\" \n"
-                + "            ]   \n"
-                + "        },\n"
-                + "        \"2\": {\n"
-                + "            \"p\": 2000,\n"
-                + "            \"stuff\": [\n"
-                + "            ]   \n"
-                + "        }\n"
-                + "    },\n"
-                + "    \"anumber\": 25385874\n"
-                + "}";
-
-        Foo obj = mapper.readValue(JSON, Foo.class);
-        assertNotNull(obj);
-        assertNotNull(obj.foo);
-        assertEquals(3, obj.foo.size());
-        assertEquals(25385874L, obj.getAnumber());
-    }
-}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/AnySetter349Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/AnySetter349Test.java
index 5ff1d2b..7cfa4d4 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/AnySetter349Test.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/AnySetter349Test.java
@@ -12,6 +12,7 @@
     static class Bean349
     {
         public String type;
+        public int x, y;
     
         private Map<String, Object> props = new HashMap<>();
     
@@ -32,18 +33,34 @@
     static class IdentityDTO349 {
         public int x, y;
     }
+
+    final static String UNWRAPPED_JSON_349 = aposToQuotes(
+"{ 'type' : 'IST',\n"
++" 'x' : 3,\n"
+//+" 'name' : 'BLAH-New',\n"
+//+" 'description' : 'namespace.name: X THIN FIR.DR-WD12-New',\n"
++" 'ZoomLinks': [ 'foofoofoofoo', 'barbarbarbar' ],\n"
++" 'y' : 4, 'z' : 8 }"
+            );
     
     public void testUnwrappedWithAny() throws Exception
     {
         final ObjectMapper mapper = objectMapper();
-        final String json = aposToQuotes(
-"{ 'type' : 'IST',\n"
-//+" 'spacename' : 'Foo Models',\n"
-//+" 'name' : 'BLAH-New',\n"
-//+" 'description' : 'namespace.name: X THIN FIR.DR-WD12-New',\n"
-+" 'ZoomLinks': [ 'foofoofoofoo', 'barbarbarbar' ] }"
-                );
-        Bean349 value = mapper.readValue(json,  Bean349.class);
+        Bean349 value = mapper.readValue(UNWRAPPED_JSON_349,  Bean349.class);
         assertNotNull(value);
+        assertEquals(3, value.x);
+        assertEquals(4, value.y);
+        assertEquals(2, value.props.size());
+    }
+
+    public void testUnwrappedWithAnyAsUpdate() throws Exception
+    {
+        final ObjectMapper mapper = objectMapper();
+        Bean349 bean = mapper.readerFor(Bean349.class)
+                .withValueToUpdate(new Bean349())
+                .readValue(UNWRAPPED_JSON_349);
+        assertEquals(3, bean.x);
+        assertEquals(4, bean.y);
+        assertEquals(2, bean.props.size());
     }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestAnyProperties.java b/src/test/java/com/fasterxml/jackson/databind/deser/AnySetterTest.java
similarity index 62%
rename from src/test/java/com/fasterxml/jackson/databind/deser/TestAnyProperties.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/AnySetterTest.java
index fb7ef8e..7e5dc85 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestAnyProperties.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/AnySetterTest.java
@@ -10,7 +10,7 @@
  * Unit tests for verifying that {@link JsonAnySetter} annotation
  * works as expected.
  */
-public class TestAnyProperties
+public class AnySetterTest
     extends BaseMapTest
 {
     static class MapImitator
@@ -28,6 +28,16 @@
         }
     }
 
+    // for [databind#1376]
+    static class MapImitatorDisabled extends MapImitator
+    {
+        @Override
+        @JsonAnySetter(enabled=false)
+        void addEntry(String key, Object value) {
+            throw new RuntimeException("Should not get called");
+        }
+    }
+
     /**
      * Let's also verify that it is possible to define different
      * value: not often useful, but possible.
@@ -135,34 +145,77 @@
         }
     }
     
-	static class JsonAnySetterOnMap {
-		public int id;
+    static class JsonAnySetterOnMap {
+        public int id;
 
-		@JsonAnySetter
-		protected HashMap<String, String> other = new HashMap<String, String>();
+        @JsonAnySetter
+        protected HashMap<String, String> other = new HashMap<String, String>();
 
-		@JsonAnyGetter
-		public Map<String, String> any() {
-			return other;
-		}
+        @JsonAnyGetter
+        public Map<String, String> any() {
+            return other;
+        }
+    }
 
-	}
+    static class JsonAnySetterOnNullMap {
+        public int id;
 
-	static class JsonAnySetterOnNullMap {
-		public int id;
+        @JsonAnySetter
+        protected HashMap<String, String> other;
 
-		@JsonAnySetter
-		protected HashMap<String, String> other;
+        @JsonAnyGetter
+        public Map<String, String> any() {
+            return other;
+        }
+    }
 
-		@JsonAnyGetter
-		public Map<String, String> any() {
-			return other;
-		}
-
-	}
-
+    static class MyGeneric<T>
+    {
+        private String staticallyMappedProperty;
+        private Map<T, Integer> dynamicallyMappedProperties = new HashMap<T, Integer>();
     
-    /*
+        public String getStaticallyMappedProperty() {
+            return staticallyMappedProperty;
+        }
+    
+        @JsonAnySetter
+        public void addDynamicallyMappedProperty(T key, int value) {
+            dynamicallyMappedProperties.put(key, value);
+        }
+
+        public void setStaticallyMappedProperty(String staticallyMappedProperty) {
+            this.staticallyMappedProperty = staticallyMappedProperty;
+        }
+    
+        @JsonAnyGetter
+        public Map<T, Integer> getDynamicallyMappedProperties() {
+            return dynamicallyMappedProperties;
+        }
+    }
+
+    static class MyWrapper
+    {
+        private MyGeneric<String> myStringGeneric;
+        private MyGeneric<Integer> myIntegerGeneric;
+
+        public MyGeneric<String> getMyStringGeneric() {
+            return myStringGeneric;
+        }
+
+        public void setMyStringGeneric(MyGeneric<String> myStringGeneric) {
+            this.myStringGeneric = myStringGeneric;
+        }
+
+        public MyGeneric<Integer> getMyIntegerGeneric() {
+            return myIntegerGeneric;
+        }
+
+        public void setMyIntegerGeneric(MyGeneric<Integer> myIntegerGeneric) {
+            this.myIntegerGeneric = myIntegerGeneric;
+        }
+    }
+
+	/*
     /**********************************************************
     /* Test methods
     /**********************************************************
@@ -185,6 +238,18 @@
         assertEquals(Integer.valueOf(3), l.get(2));
     }
 
+    public void testAnySetterDisable() throws Exception
+    {
+        try {
+            MAPPER.readValue(aposToQuotes("{'value':3}"),
+                    MapImitatorDisabled.class);
+            fail("Should not pass");
+        } catch (JsonMappingException e) {
+            verifyException(e, "Unrecognized field \"value\"");
+        }
+
+    }
+
     public void testSimpleTyped() throws Exception
     {
         MapImitatorWithValue mapHolder = MAPPER.readValue
@@ -202,7 +267,7 @@
             Broken b = MAPPER.readValue("{ \"a\" : 3 }", Broken.class);
             fail("Should have gotten an exception");
         } catch (JsonMappingException e) {
-            verifyException(e, "Multiple 'any-setters'");
+            verifyException(e, "Multiple 'any-setter' methods");
         }
     }
 
@@ -239,6 +304,7 @@
     {
         PolyAnyBean input = new PolyAnyBean();
         input.props.put("a", new Impl("xyz"));
+
         String json = MAPPER.writeValueAsString(input);
         
 //        System.err.println("JSON: "+json);
@@ -264,8 +330,43 @@
 		        JsonAnySetterOnNullMap.class);
 		assertEquals(2, result.id);
 		assertNull(result.other);
-	}
-    
+    }
+
+    // [databind#1035]
+    public void testGenericAnySetter() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+
+        Map<String, Integer> stringGenericMap = new HashMap<String, Integer>();
+        stringGenericMap.put("testStringKey", 5);
+        Map<Integer, Integer> integerGenericMap = new HashMap<Integer, Integer>();
+        integerGenericMap.put(111, 6);
+
+        MyWrapper deserialized = mapper.readValue(aposToQuotes(
+                "{'myStringGeneric':{'staticallyMappedProperty':'Test','testStringKey':5},'myIntegerGeneric':{'staticallyMappedProperty':'Test2','111':6}}"
+                ), MyWrapper.class);
+        MyGeneric<String> stringGeneric = deserialized.getMyStringGeneric();
+        MyGeneric<Integer> integerGeneric = deserialized.getMyIntegerGeneric();
+
+        assertNotNull(stringGeneric);
+        assertEquals(stringGeneric.getStaticallyMappedProperty(), "Test");
+        for(Map.Entry<String, Integer> entry : stringGeneric.getDynamicallyMappedProperties().entrySet()) {
+            assertTrue("A key in MyGeneric<String> is not an String.", entry.getKey() instanceof String);
+            assertTrue("A value in MyGeneric<Integer> is not an Integer.", entry.getValue() instanceof Integer);
+        }
+        assertEquals(stringGeneric.getDynamicallyMappedProperties(), stringGenericMap);
+
+        assertNotNull(integerGeneric);
+        assertEquals(integerGeneric.getStaticallyMappedProperty(), "Test2");
+        for(Map.Entry<Integer, Integer> entry : integerGeneric.getDynamicallyMappedProperties().entrySet()) {
+            Object key = entry.getKey();
+            assertEquals("A key in MyGeneric<Integer> is not an Integer.", Integer.class, key.getClass());
+            Object value = entry.getValue();
+            assertEquals("A value in MyGeneric<Integer> is not an Integer.", Integer.class, value.getClass());
+        }
+        assertEquals(integerGeneric.getDynamicallyMappedProperties(), integerGenericMap);
+    }
+
     /*
     /**********************************************************
     /* Private helper methods
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestAnnotationIgnore.java b/src/test/java/com/fasterxml/jackson/databind/deser/IgnoreWithDeserTest.java
similarity index 98%
rename from src/test/java/com/fasterxml/jackson/databind/deser/TestAnnotationIgnore.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/IgnoreWithDeserTest.java
index 74c656c..e1dfa5b 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestAnnotationIgnore.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/IgnoreWithDeserTest.java
@@ -8,7 +8,7 @@
  * This unit test suite that tests use of {@link JsonIgnore}
  * annotation with deserialization.
  */
-public class TestAnnotationIgnore
+public class IgnoreWithDeserTest
     extends BaseMapTest
 {
     // Class for testing {@link JsonIgnore} annotations with setters
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/JDKScalarsTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/JDKScalarsTest.java
deleted file mode 100644
index 7679745..0000000
--- a/src/test/java/com/fasterxml/jackson/databind/deser/JDKScalarsTest.java
+++ /dev/null
@@ -1,1002 +0,0 @@
-package com.fasterxml.jackson.databind.deser;
-
-import java.io.*;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-
-import org.junit.Assert;
-
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.core.*;
-import com.fasterxml.jackson.databind.*;
-
-/**
- * Unit tests for verifying handling of simple basic non-structured
- * types; primitives (and/or their wrappers), Strings.
- */
-public class JDKScalarsTest
-    extends BaseMapTest
-{
-    final static String NAN_STRING = "NaN";
-
-    final static class BooleanBean {
-        boolean _v;
-        void setV(boolean v) { _v = v; }
-    }
-
-    static class BooleanWrapper {
-        public Boolean wrapper;
-        public boolean primitive;
-        
-        protected Boolean ctor;
-        
-        @JsonCreator
-        public BooleanWrapper(@JsonProperty("ctor") Boolean foo) {
-            ctor = foo;
-        }
-    }
-    
-    static class IntBean {
-        int _v;
-        void setV(int v) { _v = v; }
-    }
-
-    final static class DoubleBean {
-        double _v;
-        void setV(double v) { _v = v; }
-    }
-
-    final static class FloatBean {
-        float _v;
-        void setV(float v) { _v = v; }
-    }
-    
-    final static class CharacterBean {
-        char _v;
-        void setV(char v) { _v = v; }
-        char getV() { return _v; }
-    }
-    
-    final static class CharacterWrapperBean {
-        Character _v;
-        void setV(Character v) { _v = v; }
-        Character getV() { return _v; }
-    }
-
-    /**
-     * Also, let's ensure that it's ok to override methods.
-     */
-    static class IntBean2
-        extends IntBean
-    {
-        @Override
-        void setV(int v2) { super.setV(v2+1); }
-    }
-
-    static class PrimitivesBean
-    {
-        public boolean booleanValue = true;
-        public byte byteValue = 3;
-        public char charValue = 'a';
-        public short shortValue = 37;
-        public int intValue = 1;
-        public long longValue = 100L;
-        public float floatValue = 0.25f;
-        public double doubleValue = -1.0;
-    }
-
-    static class WrappersBean
-    {
-        public Boolean booleanValue;
-        public Byte byteValue;
-        public Character charValue;
-        public Short shortValue;
-        public Integer intValue;
-        public Long longValue;
-        public Float floatValue;
-        public Double doubleValue;
-    }
-
-    private final ObjectMapper MAPPER = new ObjectMapper();
-
-    /*
-    /**********************************************************
-    /* Scalar tests for boolean
-    /**********************************************************
-     */
-
-    public void testBooleanPrimitive() throws Exception
-    {
-        // first, simple case:
-        BooleanBean result = MAPPER.readValue(new StringReader("{\"v\":true}"), BooleanBean.class);
-        assertTrue(result._v);
-        result = MAPPER.readValue(new StringReader("{\"v\":null}"), BooleanBean.class);
-        assertNotNull(result);
-        assertFalse(result._v);
-        // [databind#1480]
-        result = MAPPER.readValue(new StringReader("{\"v\":1}"), BooleanBean.class);
-        assertNotNull(result);
-        assertTrue(result._v);
-
-        // should work with arrays too..
-        boolean[] array = MAPPER.readValue(new StringReader("[ null ]"), boolean[].class);
-        assertNotNull(array);
-        assertEquals(1, array.length);
-        assertFalse(array[0]);
-
-    }
-        
-    public void testBooleanPrimitiveArrayUnwrap() throws Exception
-    {
-        // [databind#381]
-        final ObjectMapper mapper = new ObjectMapper();
-        mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
-        BooleanBean result = mapper.readValue(new StringReader("{\"v\":[true]}"), BooleanBean.class);
-        assertTrue(result._v);
-        
-        try {
-            mapper.readValue(new StringReader("[{\"v\":[true,true]}]"), BooleanBean.class);
-            fail("Did not throw exception while reading a value from a multi value array with UNWRAP_SINGLE_VALUE_ARRAY feature enabled");
-        } catch (JsonMappingException exp) {
-            //threw exception as required
-        }
-        
-        result = mapper.readValue(new StringReader("{\"v\":[null]}"), BooleanBean.class);
-        assertNotNull(result);
-        assertFalse(result._v);
-        
-        result = mapper.readValue(new StringReader("[{\"v\":[null]}]"), BooleanBean.class);
-        assertNotNull(result);
-        assertFalse(result._v);
-        
-        boolean[] array = mapper.readValue(new StringReader("[ [ null ] ]"), boolean[].class);
-        assertNotNull(array);
-        assertEquals(1, array.length);
-        assertFalse(array[0]);
-    }
-
-    /**
-     * Simple unit test to verify that we can map boolean values to
-     * java.lang.Boolean.
-     */
-    public void testBooleanWrapper() throws Exception
-    {
-        Boolean result = MAPPER.readValue(new StringReader("true"), Boolean.class);
-        assertEquals(Boolean.TRUE, result);
-        result = MAPPER.readValue(new StringReader("false"), Boolean.class);
-        assertEquals(Boolean.FALSE, result);
-
-        // should accept ints too, (0 == false, otherwise true)
-        result = MAPPER.readValue("0", Boolean.class);
-        assertEquals(Boolean.FALSE, result);
-        result = MAPPER.readValue("1", Boolean.class);
-        assertEquals(Boolean.TRUE, result);
-    }
-
-    // Test for verifying that Long values are coerced to boolean correctly as well
-    public void testLongToBoolean() throws Exception
-    {
-        long value = 1L + Integer.MAX_VALUE;
-        BooleanWrapper b = MAPPER.readValue("{\"primitive\" : "+value+", \"wrapper\":"+value+", \"ctor\":"+value+"}",
-                BooleanWrapper.class);
-        assertEquals(Boolean.TRUE, b.wrapper);
-        assertTrue(b.primitive);
-        assertEquals(Boolean.TRUE, b.ctor);
-
-        // but ensure we can also get `false`
-        b = MAPPER.readValue("{\"primitive\" : 0 , \"wrapper\":0, \"ctor\":0}",
-                BooleanWrapper.class);
-        assertEquals(Boolean.FALSE, b.wrapper);
-        assertFalse(b.primitive);
-        assertEquals(Boolean.FALSE, b.ctor);
-    }
-
-    /*
-    /**********************************************************
-    /* Scalar tests for integral types
-    /**********************************************************
-     */
-
-    public void testIntPrimitive() throws Exception
-    {
-        // first, simple case:
-        IntBean result = MAPPER.readValue(new StringReader("{\"v\":3}"), IntBean.class);
-        assertEquals(3, result._v);
-        result = MAPPER.readValue(new StringReader("{\"v\":null}"), IntBean.class);
-        assertNotNull(result);
-        assertEquals(0, result._v);
-
-        // should work with arrays too..
-        int[] array = MAPPER.readValue(new StringReader("[ null ]"), int[].class);
-        assertNotNull(array);
-        assertEquals(1, array.length);
-        assertEquals(0, array[0]);
-        
-        // [Issue#381]
-        final ObjectMapper mapper = new ObjectMapper();
-        mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
-        try {
-            mapper.readValue(new StringReader("{\"v\":[3]}"), IntBean.class);
-            fail("Did not throw exception when reading a value from a single value array with the UNWRAP_SINGLE_VALUE_ARRAYS feature disabled");
-        } catch (JsonMappingException exp) {
-            //Correctly threw exception
-        }
-        
-        mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
-        
-        result = mapper.readValue(new StringReader("{\"v\":[3]}"), IntBean.class);
-        assertEquals(3, result._v);
-        
-        result = mapper.readValue(new StringReader("[{\"v\":[3]}]"), IntBean.class);
-        assertEquals(3, result._v);
-        
-        try {
-            mapper.readValue("[{\"v\":[3,3]}]", IntBean.class);
-            fail("Did not throw exception while reading a value from a multi value array with UNWRAP_SINGLE_VALUE_ARRAY feature enabled");
-        } catch (JsonMappingException exp) {
-            //threw exception as required
-        }
-        
-        result = mapper.readValue("{\"v\":[null]}", IntBean.class);
-        assertNotNull(result);
-        assertEquals(0, result._v);
-
-        array = mapper.readValue("[ [ null ] ]", int[].class);
-        assertNotNull(array);
-        assertEquals(1, array.length);
-        assertEquals(0, array[0]);
-    }
-
-    public void testByteWrapper() throws Exception
-    {
-        Byte result = MAPPER.readValue(new StringReader("   -42\t"), Byte.class);
-        assertEquals(Byte.valueOf((byte)-42), result);
-
-        // Also: should be able to coerce floats, strings:
-        result = MAPPER.readValue(new StringReader(" \"-12\""), Byte.class);
-        assertEquals(Byte.valueOf((byte)-12), result);
-
-        result = MAPPER.readValue(new StringReader(" 39.07"), Byte.class);
-        assertEquals(Byte.valueOf((byte)39), result);
-    }
-
-    public void testShortWrapper() throws Exception
-    {
-        Short result = MAPPER.readValue(new StringReader("37"), Short.class);
-        assertEquals(Short.valueOf((short)37), result);
-
-        // Also: should be able to coerce floats, strings:
-        result = MAPPER.readValue(new StringReader(" \"-1009\""), Short.class);
-        assertEquals(Short.valueOf((short)-1009), result);
-
-        result = MAPPER.readValue(new StringReader("-12.9"), Short.class);
-        assertEquals(Short.valueOf((short)-12), result);
-    }
-
-    public void testCharacterWrapper() throws Exception
-    {
-        // First: canonical value is 1-char string
-        Character result = MAPPER.readValue(new StringReader("\"a\""), Character.class);
-        assertEquals(Character.valueOf('a'), result);
-
-        // But can also pass in ascii code
-        result = MAPPER.readValue(new StringReader(" "+((int) 'X')), Character.class);
-        assertEquals(Character.valueOf('X'), result);
-        
-        final CharacterWrapperBean wrapper = MAPPER.readValue(new StringReader("{\"v\":null}"), CharacterWrapperBean.class);
-        assertNotNull(wrapper);
-        assertNull(wrapper.getV());
-        
-        final ObjectMapper mapper = new ObjectMapper();
-        mapper.enable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES);
-        try {
-            mapper.readValue("{\"v\":null}", CharacterBean.class);
-            fail("Attempting to deserialize a 'null' JSON reference into a 'char' property did not throw an exception");
-        } catch (JsonMappingException exp) {
-            //Exception thrown as required
-        }
-
-        mapper.disable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES);  
-        final CharacterBean charBean = MAPPER.readValue(new StringReader("{\"v\":null}"), CharacterBean.class);
-        assertNotNull(wrapper);
-        assertEquals('\u0000', charBean.getV());
-    }
-
-    public void testIntWrapper() throws Exception
-    {
-        Integer result = MAPPER.readValue(new StringReader("   -42\t"), Integer.class);
-        assertEquals(Integer.valueOf(-42), result);
-
-        // Also: should be able to coerce floats, strings:
-        result = MAPPER.readValue(new StringReader(" \"-1200\""), Integer.class);
-        assertEquals(Integer.valueOf(-1200), result);
-
-        result = MAPPER.readValue(new StringReader(" 39.07"), Integer.class);
-        assertEquals(Integer.valueOf(39), result);
-    }
-
-    public void testLongWrapper() throws Exception
-    {
-        Long result = MAPPER.readValue(new StringReader("12345678901"), Long.class);
-        assertEquals(Long.valueOf(12345678901L), result);
-
-        // Also: should be able to coerce floats, strings:
-        result = MAPPER.readValue(new StringReader(" \"-9876\""), Long.class);
-        assertEquals(Long.valueOf(-9876), result);
-
-        result = MAPPER.readValue(new StringReader("1918.3"), Long.class);
-        assertEquals(Long.valueOf(1918), result);
-    }
-    
-    /**
-     * Beyond simple case, let's also ensure that method overriding works as
-     * expected.
-     */
-    public void testIntWithOverride() throws Exception
-    {
-        IntBean2 result = MAPPER.readValue(new StringReader("{\"v\":8}"), IntBean2.class);
-        assertEquals(9, result._v);
-    }
-
-    /*
-    /**********************************************************
-    /* Scalar tests for floating point types
-    /**********************************************************
-     */
-
-    public void testDoublePrimitive() throws Exception
-    {
-        // first, simple case:
-        // bit tricky with binary fps but...
-        final double value = 0.016;
-        DoubleBean result = MAPPER.readValue(new StringReader("{\"v\":"+value+"}"), DoubleBean.class);
-        assertEquals(value, result._v);
-        // then [JACKSON-79]:
-        result = MAPPER.readValue(new StringReader("{\"v\":null}"), DoubleBean.class);
-        assertNotNull(result);
-        assertEquals(0.0, result._v);
-
-        // should work with arrays too..
-        double[] array = MAPPER.readValue(new StringReader("[ null ]"), double[].class);
-        assertNotNull(array);
-        assertEquals(1, array.length);
-        assertEquals(0.0, array[0]);
-    }
-
-    /* Note: dealing with floating-point values is tricky; not sure if
-     * we can really use equality tests here... JDK does have decent
-     * conversions though, to retain accuracy and round-trippability.
-     * But still...
-     */
-    public void testFloatWrapper() throws Exception
-    {
-        // Also: should be able to coerce floats, strings:
-        String[] STRS = new String[] {
-            "1.0", "0.0", "-0.3", "0.7", "42.012", "-999.0", NAN_STRING
-        };
-
-        for (String str : STRS) {
-            Float exp = Float.valueOf(str);
-            Float result;
-
-            if (NAN_STRING != str) {
-                // First, as regular floating point value
-                result = MAPPER.readValue(new StringReader(str), Float.class);
-                assertEquals(exp, result);
-            }
-
-            // and then as coerced String:
-            result = MAPPER.readValue(new StringReader(" \""+str+"\""), Float.class);
-            assertEquals(exp, result);
-        }
-    }
-
-    public void testDoubleWrapper() throws Exception
-    {
-        // Also: should be able to coerce doubles, strings:
-        String[] STRS = new String[] {
-            "1.0", "0.0", "-0.3", "0.7", "42.012", "-999.0", NAN_STRING
-        };
-
-        for (String str : STRS) {
-            Double exp = Double.valueOf(str);
-            Double result;
-
-            // First, as regular double value
-            if (NAN_STRING != str) {
-                result = MAPPER.readValue(str, Double.class);
-               assertEquals(exp, result);
-            }
-            // and then as coerced String:
-            result = MAPPER.readValue(new StringReader(" \""+str+"\""), Double.class);
-            assertEquals(exp, result);
-        }
-    }
-
-    public void testDoubleAsArray() throws Exception
-    {
-        final ObjectMapper mapper = new ObjectMapper();
-        mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
-        final double value = 0.016;
-        try {
-            mapper.readValue(new StringReader("{\"v\":[" + value + "]}"), DoubleBean.class);
-            fail("Did not throw exception when reading a value from a single value array with the UNWRAP_SINGLE_VALUE_ARRAYS feature disabled");
-        } catch (JsonMappingException exp) {
-            //Correctly threw exception
-        }
-        
-        mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
-        
-        DoubleBean result = mapper.readValue(new StringReader("{\"v\":[" + value + "]}"),
-                DoubleBean.class);
-        assertEquals(value, result._v);
-        
-        result = mapper.readValue(new StringReader("[{\"v\":[" + value + "]}]"), DoubleBean.class);
-        assertEquals(value, result._v);
-        
-        try {
-            mapper.readValue(new StringReader("[{\"v\":[" + value + "," + value + "]}]"), DoubleBean.class);
-            fail("Did not throw exception while reading a value from a multi value array with UNWRAP_SINGLE_VALUE_ARRAY feature enabled");
-        } catch (JsonMappingException exp) {
-            //threw exception as required
-        }
-        
-        result = mapper.readValue(new StringReader("{\"v\":[null]}"), DoubleBean.class);
-        assertNotNull(result);
-        assertEquals(0d, result._v);
-
-        double[] array = mapper.readValue(new StringReader("[ [ null ] ]"), double[].class);
-        assertNotNull(array);
-        assertEquals(1, array.length);
-        assertEquals(0d, array[0]);
-    }
-
-    public void testDoublePrimitiveNonNumeric() throws Exception
-    {
-        // first, simple case:
-        // bit tricky with binary fps but...
-        double value = Double.POSITIVE_INFINITY;
-        DoubleBean result = MAPPER.readValue(new StringReader("{\"v\":\""+value+"\"}"), DoubleBean.class);
-        assertEquals(value, result._v);
-        
-        // should work with arrays too..
-        double[] array = MAPPER.readValue(new StringReader("[ \"Infinity\" ]"), double[].class);
-        assertNotNull(array);
-        assertEquals(1, array.length);
-        assertEquals(Double.POSITIVE_INFINITY, array[0]);
-    }
-    
-    public void testFloatPrimitiveNonNumeric() throws Exception
-    {
-        // bit tricky with binary fps but...
-        float value = Float.POSITIVE_INFINITY;
-        FloatBean result = MAPPER.readValue(new StringReader("{\"v\":\""+value+"\"}"), FloatBean.class);
-        assertEquals(value, result._v);
-        
-        // should work with arrays too..
-        float[] array = MAPPER.readValue(new StringReader("[ \"Infinity\" ]"), float[].class);
-        assertNotNull(array);
-        assertEquals(1, array.length);
-        assertEquals(Float.POSITIVE_INFINITY, array[0]);
-    }
-
-    /*
-    /**********************************************************
-    /* Scalar tests, other
-    /**********************************************************
-     */
-
-    public void testEmptyToNullCoercionForPrimitives() throws Exception {
-        _testEmptyToNullCoercion(int.class, Integer.valueOf(0));
-        _testEmptyToNullCoercion(long.class, Long.valueOf(0));
-        _testEmptyToNullCoercion(double.class, Double.valueOf(0.0));
-        _testEmptyToNullCoercion(float.class, Float.valueOf(0.0f));
-    }
-
-    private void _testEmptyToNullCoercion(Class<?> primType, Object emptyValue) throws Exception
-    {
-        final String EMPTY = "\"\"";
-
-        // as per [databind#1095] should only allow coercion from empty String,
-        // if `null` is acceptable
-        ObjectReader intR = MAPPER.readerFor(primType);
-        assertEquals(emptyValue, intR.readValue(EMPTY));
-        try {
-            intR.with(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)
-                .readValue("\"\"");
-            fail("Should not have passed");
-        } catch (JsonMappingException e) {
-            verifyException(e, "Can not map Empty String");
-        }
-    }
-
-    public void testBase64Variants() throws Exception
-    {
-        final byte[] INPUT = "abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyz1234567890X".getBytes("UTF-8");
-        
-        // default encoding is "MIME, no linefeeds", so:
-        Assert.assertArrayEquals(INPUT, MAPPER.readValue(
-                quote("YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwWA=="),
-                byte[].class));
-        ObjectReader reader = MAPPER.readerFor(byte[].class);
-        Assert.assertArrayEquals(INPUT, (byte[]) reader.with(Base64Variants.MIME_NO_LINEFEEDS).readValue(
-                quote("YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwWA=="
-        )));
-
-        // but others should be slightly different
-        Assert.assertArrayEquals(INPUT, (byte[]) reader.with(Base64Variants.MIME).readValue(
-                quote("YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwYWJjZGVmZ2hpamtsbW5vcHFyc3R1\\ndnd4eXoxMjM0NTY3ODkwWA=="
-        )));
-        Assert.assertArrayEquals(INPUT, (byte[]) reader.with(Base64Variants.MODIFIED_FOR_URL).readValue(
-                quote("YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwWA"
-        )));
-        // PEM mandates 64 char lines:
-        Assert.assertArrayEquals(INPUT, (byte[]) reader.with(Base64Variants.PEM).readValue(
-                quote("YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwYWJjZGVmZ2hpamts\\nbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwWA=="
-        )));
-    }    
-    /*
-    /**********************************************************
-    /* Simple non-primitive types
-    /**********************************************************
-     */
-
-    public void testSingleString() throws Exception
-    {
-        String value = "FOO!";
-        String result = MAPPER.readValue(new StringReader("\""+value+"\""), String.class);
-        assertEquals(value, result);
-    }
-    
-    public void testSingleStringWrapped() throws Exception
-    {
-        final ObjectMapper mapper = new ObjectMapper();
-        mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
-        
-        String value = "FOO!";
-        try {
-            mapper.readValue(new StringReader("[\""+value+"\"]"), String.class);
-            fail("Exception not thrown when attempting to unwrap a single value 'String' array into a simple String");
-        } catch (JsonMappingException exp) {
-            //exception thrown correctly
-        }
-        
-        mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
-        
-        try {
-            mapper.readValue(new StringReader("[\""+value+"\",\""+value+"\"]"), String.class);
-            fail("Exception not thrown when attempting to unwrap a single value 'String' array that contained more than one value into a simple String");
-        } catch (JsonMappingException exp) {
-            //exception thrown correctly
-        }
-        
-        String result = mapper.readValue(new StringReader("[\""+value+"\"]"), String.class);
-        assertEquals(value, result);
-    }
-
-    public void testBigDecimal() throws Exception
-    {
-        final ObjectMapper mapper = objectMapper();
-        mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
-        
-        BigDecimal value = new BigDecimal("0.001");
-        BigDecimal result = mapper.readValue(value.toString(), BigDecimal.class);
-        assertEquals(value, result);
-        try {
-            mapper.readValue("[" + value.toString() + "]", BigDecimal.class);
-            fail("Exception was not thrown when attempting to read a single value array of BigDecimal when UNWRAP_SINGLE_VALUE_ARRAYS feature is disabled");
-        } catch (JsonMappingException exp) {
-            //Exception was thrown correctly
-        }
-        
-        mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
-        result = mapper.readValue("[" + value.toString() + "]", BigDecimal.class);
-        assertEquals(value, result);
-        
-        try {
-            mapper.readValue("[" + value.toString() + "," + value.toString() + "]", BigDecimal.class);
-            fail("Exception was not thrown when attempting to read a muti value array of BigDecimal when UNWRAP_SINGLE_VALUE_ARRAYS feature is enabled");
-        } catch (JsonMappingException exp) {
-            //Exception was thrown correctly
-        }
-    }
-
-    public void testBigInteger() throws Exception
-    {
-        final ObjectMapper mapper = objectMapper();
-        mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
-        
-        BigInteger value = new BigInteger("-1234567890123456789012345567809");
-        BigInteger result = mapper.readValue(new StringReader(value.toString()), BigInteger.class);
-        assertEquals(value, result);
-        
-        //Issue#381
-        try {
-            mapper.readValue("[" + value.toString() + "]", BigInteger.class);
-            fail("Exception was not thrown when attempting to read a single value array of BigInteger when UNWRAP_SINGLE_VALUE_ARRAYS feature is disabled");
-        } catch (JsonMappingException exp) {
-            //Exception was thrown correctly
-        }
-        
-        mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
-        result = mapper.readValue("[" + value.toString() + "]", BigInteger.class);
-        assertEquals(value, result);
-        
-        try {
-            mapper.readValue("[" + value.toString() + "," + value.toString() + "]", BigInteger.class);
-            fail("Exception was not thrown when attempting to read a muti value array of BigInteger when UNWRAP_SINGLE_VALUE_ARRAYS feature is enabled");
-        } catch (JsonMappingException exp) {
-            //Exception was thrown correctly
-        }        
-    }
-
-    /*
-    /**********************************************************
-    /* Sequence tests
-    /**********************************************************
-     */
-
-    /**
-     * Then a unit test to verify that we can conveniently bind sequence of
-     * space-separate simple values
-     */
-    public void testSequenceOfInts() throws Exception
-    {
-        final int NR_OF_INTS = 100;
-
-        StringBuilder sb = new StringBuilder();
-        for (int i = 0; i < NR_OF_INTS; ++i) {
-            sb.append(" ");
-            sb.append(i);
-        }
-        JsonParser jp = MAPPER.getFactory().createParser(sb.toString());
-        for (int i = 0; i < NR_OF_INTS; ++i) {
-            Integer result = MAPPER.readValue(jp, Integer.class);
-            assertEquals(Integer.valueOf(i), result);
-        }
-        jp.close();
-    }
-
-    /*
-    /**********************************************************
-    /* Single-element as array tests
-    /**********************************************************
-     */
-    
-    // [databind#381]
-    public void testSingleElementScalarArrays() throws Exception {
-        final int intTest = 932832;
-        final double doubleTest = 32.3234;
-        final long longTest = 2374237428374293423L;
-        final short shortTest = (short) intTest;
-        final float floatTest = 84.3743f;
-        final byte byteTest = (byte) 43;
-        final char charTest = 'c';
-
-        final ObjectMapper mapper = new ObjectMapper();
-        mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
-
-        final int intValue = mapper.readValue(asArray(intTest), Integer.TYPE);
-        assertEquals(intTest, intValue);
-        final Integer integerWrapperValue = mapper.readValue(asArray(Integer.valueOf(intTest)), Integer.class);
-        assertEquals(Integer.valueOf(intTest), integerWrapperValue);
-
-        final double doubleValue = mapper.readValue(asArray(doubleTest), Double.class);
-        assertEquals(doubleTest, doubleValue);
-        final Double doubleWrapperValue = mapper.readValue(asArray(Double.valueOf(doubleTest)), Double.class);
-        assertEquals(Double.valueOf(doubleTest), doubleWrapperValue);
-
-        final long longValue = mapper.readValue(asArray(longTest), Long.TYPE);
-        assertEquals(longTest, longValue);
-        final Long longWrapperValue = mapper.readValue(asArray(Long.valueOf(longTest)), Long.class);
-        assertEquals(Long.valueOf(longTest), longWrapperValue);
-
-        final short shortValue = mapper.readValue(asArray(shortTest), Short.TYPE);
-        assertEquals(shortTest, shortValue);
-        final Short shortWrapperValue = mapper.readValue(asArray(Short.valueOf(shortTest)), Short.class);
-        assertEquals(Short.valueOf(shortTest), shortWrapperValue);
-
-        final float floatValue = mapper.readValue(asArray(floatTest), Float.TYPE);
-        assertEquals(floatTest, floatValue);
-        final Float floatWrapperValue = mapper.readValue(asArray(Float.valueOf(floatTest)), Float.class);
-        assertEquals(Float.valueOf(floatTest), floatWrapperValue);
-
-        final byte byteValue = mapper.readValue(asArray(byteTest), Byte.TYPE);
-        assertEquals(byteTest, byteValue);
-        final Byte byteWrapperValue = mapper.readValue(asArray(Byte.valueOf(byteTest)), Byte.class);
-        assertEquals(Byte.valueOf(byteTest), byteWrapperValue);
-
-        final char charValue = mapper.readValue(asArray(quote(String.valueOf(charTest))), Character.TYPE);
-        assertEquals(charTest, charValue);
-        final Character charWrapperValue = mapper.readValue(asArray(quote(String.valueOf(charTest))), Character.class);
-        assertEquals(Character.valueOf(charTest), charWrapperValue);
-
-        final boolean booleanTrueValue = mapper.readValue(asArray(true), Boolean.TYPE);
-        assertTrue(booleanTrueValue);
-
-        final boolean booleanFalseValue = mapper.readValue(asArray(false), Boolean.TYPE);
-        assertFalse(booleanFalseValue);
-
-        final Boolean booleanWrapperTrueValue = mapper.readValue(asArray(Boolean.valueOf(true)), Boolean.class);
-        assertEquals(Boolean.TRUE, booleanWrapperTrueValue);
-    }
-
-    public void testSingleElementArrayDisabled() throws Exception {
-        final ObjectMapper mapper = new ObjectMapper();
-        mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
-        try {
-            mapper.readValue("[42]", Integer.class);
-            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
-        } catch (JsonMappingException exp) {
-            //Exception was thrown correctly
-        }
-        try {
-            mapper.readValue("[42]", Integer.TYPE);
-            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
-        } catch (JsonMappingException exp) {
-            //Exception was thrown correctly
-        }
-
-        try {
-            mapper.readValue("[42.273]", Double.class);
-            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
-        } catch (JsonMappingException exp) {
-            //Exception was thrown correctly
-        }
-        try {
-            mapper.readValue("[42.2723]", Double.TYPE);
-            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
-        } catch (JsonMappingException exp) {
-            //Exception was thrown correctly
-        }
-
-        try {
-            mapper.readValue("[42342342342342]", Long.class);
-            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
-        } catch (JsonMappingException exp) {
-            //Exception was thrown correctly
-        }
-        try {
-            mapper.readValue("[42342342342342342]", Long.TYPE);
-            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
-        } catch (JsonMappingException exp) {
-            //Exception was thrown correctly
-        }
-
-        try {
-            mapper.readValue("[42]", Short.class);
-            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
-        } catch (JsonMappingException exp) {
-            //Exception was thrown correctly
-        }
-        try {
-            mapper.readValue("[42]", Short.TYPE);
-            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
-        } catch (JsonMappingException exp) {
-            //Exception was thrown correctly
-        }
-
-        try {
-            mapper.readValue("[327.2323]", Float.class);
-            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
-        } catch (JsonMappingException exp) {
-            //Exception was thrown correctly
-        }
-        try {
-            mapper.readValue("[82.81902]", Float.TYPE);
-            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
-        } catch (JsonMappingException exp) {
-            //Exception was thrown correctly
-        }
-
-        try {
-            mapper.readValue("[22]", Byte.class);
-            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
-        } catch (JsonMappingException exp) {
-            //Exception was thrown correctly
-        }
-        try {
-            mapper.readValue("[22]", Byte.TYPE);
-            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
-        } catch (JsonMappingException exp) {
-            //Exception was thrown correctly
-        }
-
-        try {
-            mapper.readValue("['d']", Character.class);
-            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
-        } catch (JsonMappingException exp) {
-            //Exception was thrown correctly
-        }
-        try {
-            mapper.readValue("['d']", Character.TYPE);
-            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
-        } catch (JsonMappingException exp) {
-            //Exception was thrown correctly
-        }
-
-        try {
-            mapper.readValue("[true]", Boolean.class);
-            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
-        } catch (JsonMappingException exp) {
-            //Exception was thrown correctly
-        }
-        try {
-            mapper.readValue("[true]", Boolean.TYPE);
-            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
-        } catch (JsonMappingException exp) {
-            //Exception was thrown correctly
-        }
-    }
-
-    public void testMultiValueArrayException() throws IOException {
-        final ObjectMapper mapper = new ObjectMapper();
-        mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
-        
-        try {
-            mapper.readValue("[42,42]", Integer.class);
-            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
-        } catch (JsonMappingException exp) {
-            //Exception was thrown correctly
-        }
-        try {
-            mapper.readValue("[42,42]", Integer.TYPE);
-            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
-        } catch (JsonMappingException exp) {
-            //Exception was thrown correctly
-        }
-        
-        try {
-            mapper.readValue("[42.273,42.273]", Double.class);
-            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
-        } catch (JsonMappingException exp) {
-            //Exception was thrown correctly
-        }
-        try {
-            mapper.readValue("[42.2723,42.273]", Double.TYPE);
-            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
-        } catch (JsonMappingException exp) {
-            //Exception was thrown correctly
-        }
-        
-        try {
-            mapper.readValue("[42342342342342,42342342342342]", Long.class);
-            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
-        } catch (JsonMappingException exp) {
-            //Exception was thrown correctly
-        }
-        try {
-            mapper.readValue("[42342342342342342,42342342342342]", Long.TYPE);
-            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
-        } catch (JsonMappingException exp) {
-            //Exception was thrown correctly
-        }
-        
-        try {
-            mapper.readValue("[42,42]", Short.class);
-            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
-        } catch (JsonMappingException exp) {
-            //Exception was thrown correctly
-        }
-        try {
-            mapper.readValue("[42,42]", Short.TYPE);
-            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
-        } catch (JsonMappingException exp) {
-            //Exception was thrown correctly
-        }
-        
-        try {
-            mapper.readValue("[327.2323,327.2323]", Float.class);
-            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
-        } catch (JsonMappingException exp) {
-            //Exception was thrown correctly
-        }
-        try {
-            mapper.readValue("[82.81902,327.2323]", Float.TYPE);
-            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
-        } catch (JsonMappingException exp) {
-            //Exception was thrown correctly
-        }
-        
-        try {
-            mapper.readValue("[22,23]", Byte.class);
-            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
-        } catch (JsonMappingException exp) {
-            //Exception was thrown correctly
-        }
-        try {
-            mapper.readValue("[22,23]", Byte.TYPE);
-            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
-        } catch (JsonMappingException exp) {
-            //Exception was thrown correctly
-        }
-        
-        try {
-            mapper.readValue(asArray(quote("c") + ","  + quote("d")), Character.class);
-            
-            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
-        } catch (JsonMappingException exp) {
-            //Exception was thrown correctly
-        }
-        try {
-            mapper.readValue(asArray(quote("c") + ","  + quote("d")), Character.TYPE);
-            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
-        } catch (JsonMappingException exp) {
-            //Exception was thrown correctly
-        }
-        
-        try {
-            mapper.readValue("[true,false]", Boolean.class);
-            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
-        } catch (JsonMappingException exp) {
-            //Exception was thrown correctly
-        }
-        try {
-            mapper.readValue("[true,false]", Boolean.TYPE);
-            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
-        } catch (JsonMappingException exp) {
-            //Exception was thrown correctly
-        }
-    }
-
-    private static String asArray(Object value) {
-        final String stringVal = value.toString();
-        return new StringBuilder(stringVal.length() + 2).append("[").append(stringVal).append("]").toString();
-    }
-
-    /*
-    /**********************************************************
-    /* Empty String coercion, handling
-    /**********************************************************
-     */
-
-    // by default, should return nulls, n'est pas?
-    public void testEmptyStringForWrappers() throws IOException
-    {
-        WrappersBean bean;
-
-        // by default, ok to rely on defaults
-        bean = MAPPER.readValue("{\"booleanValue\":\"\"}", WrappersBean.class);
-        assertNull(bean.booleanValue);
-        bean = MAPPER.readValue("{\"byteValue\":\"\"}", WrappersBean.class);
-        assertNull(bean.byteValue);
-
-        // char/Character is different... not sure if this should work or not:
-        bean = MAPPER.readValue("{\"charValue\":\"\"}", WrappersBean.class);
-        assertNull(bean.charValue);
-
-        bean = MAPPER.readValue("{\"shortValue\":\"\"}", WrappersBean.class);
-        assertNull(bean.shortValue);
-        bean = MAPPER.readValue("{\"intValue\":\"\"}", WrappersBean.class);
-        assertNull(bean.intValue);
-        bean = MAPPER.readValue("{\"longValue\":\"\"}", WrappersBean.class);
-        assertNull(bean.longValue);
-        bean = MAPPER.readValue("{\"floatValue\":\"\"}", WrappersBean.class);
-        assertNull(bean.floatValue);
-        bean = MAPPER.readValue("{\"doubleValue\":\"\"}", WrappersBean.class);
-        assertNull(bean.doubleValue);
-    }
-
-    public void testEmptyStringForPrimitives() throws IOException
-    {
-        PrimitivesBean bean;
-        bean = MAPPER.readValue("{\"booleanValue\":\"\"}", PrimitivesBean.class);
-        assertFalse(bean.booleanValue);
-        bean = MAPPER.readValue("{\"byteValue\":\"\"}", PrimitivesBean.class);
-        assertEquals((byte) 0, bean.byteValue);
-        bean = MAPPER.readValue("{\"charValue\":\"\"}", PrimitivesBean.class);
-        assertEquals((char) 0, bean.charValue);
-        bean = MAPPER.readValue("{\"shortValue\":\"\"}", PrimitivesBean.class);
-        assertEquals((short) 0, bean.shortValue);
-        bean = MAPPER.readValue("{\"intValue\":\"\"}", PrimitivesBean.class);
-        assertEquals(0, bean.intValue);
-        bean = MAPPER.readValue("{\"longValue\":\"\"}", PrimitivesBean.class);
-        assertEquals(0L, bean.longValue);
-        bean = MAPPER.readValue("{\"floatValue\":\"\"}", PrimitivesBean.class);
-        assertEquals(0.0f, bean.floatValue);
-        bean = MAPPER.readValue("{\"doubleValue\":\"\"}", PrimitivesBean.class);
-        assertEquals(0.0, bean.doubleValue);
-    }
-}
-
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/KeyDeser1429Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/KeyDeser1429Test.java
deleted file mode 100644
index 7922928..0000000
--- a/src/test/java/com/fasterxml/jackson/databind/deser/KeyDeser1429Test.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package com.fasterxml.jackson.databind.deser;
-
-import java.util.Map;
-
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonValue;
-import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.*;
-
-public class KeyDeser1429Test extends BaseMapTest
-{
-    static class FullName {
-        private String _firstname, _lastname;
-
-        private FullName(String firstname, String lastname) {
-            _firstname = firstname;
-            _lastname = lastname;
-        }
-
-        @JsonCreator
-        public static FullName valueOf(String value) {
-            String[] mySplit = value.split("\\.");
-            return new FullName(mySplit[0], mySplit[1]);
-        }
-
-        public static FullName valueOf(String firstname, String lastname) {
-            return new FullName(firstname, lastname);
-        }
-
-        @JsonValue
-        @Override
-        public String toString() {
-            return _firstname + "." + _lastname;
-        }
-    }
-
-    public void testDeserializeKeyViaFactory() throws Exception
-    {
-        Map<FullName, Double> map =
-            new ObjectMapper().readValue("{\"first.last\": 42}",
-                    new TypeReference<Map<FullName, Double>>() { });
-        Map.Entry<FullName, Double> entry = map.entrySet().iterator().next();
-        FullName key = entry.getKey();
-        assertEquals(key._firstname, "first");
-        assertEquals(key._lastname, "last");
-        assertEquals(entry.getValue().doubleValue(), 42, 0);
-    }
-}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/NullHandlingTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/NullHandlingTest.java
index 21eeb3c..836e786 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/NullHandlingTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/NullHandlingTest.java
@@ -12,7 +12,6 @@
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
 import com.fasterxml.jackson.core.*;
 import com.fasterxml.jackson.databind.*;
-import com.fasterxml.jackson.databind.deser.JDKScalarsTest.PrimitivesBean;
 import com.fasterxml.jackson.databind.module.SimpleModule;
 
 public class NullHandlingTest extends BaseMapTest
@@ -138,82 +137,6 @@
         assertEquals("funny", str);
     }
 
-    public void testNullForPrimitives() throws IOException
-    {
-        // by default, ok to rely on defaults
-        PrimitivesBean bean = MAPPER.readValue(
-                "{\"intValue\":null, \"booleanValue\":null, \"doubleValue\":null}",
-                PrimitivesBean.class);
-        assertNotNull(bean);
-        assertEquals(0, bean.intValue);
-        assertEquals(false, bean.booleanValue);
-        assertEquals(0.0, bean.doubleValue);
-
-        bean = MAPPER.readValue("{\"byteValue\":null, \"longValue\":null, \"floatValue\":null}",
-                PrimitivesBean.class);
-        assertNotNull(bean);
-        assertEquals((byte) 0, bean.byteValue);
-        assertEquals(0L, bean.longValue);
-        assertEquals(0.0f, bean.floatValue);
-        
-        // but not when enabled
-        final ObjectReader reader = MAPPER
-                .readerFor(PrimitivesBean.class)
-                .with(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES);
-        // boolean
-        try {
-            reader.readValue("{\"booleanValue\":null}");
-            fail("Expected failure for boolean + null");
-        } catch (JsonMappingException e) {
-            verifyException(e, "Can not map JSON null into type boolean");
-        }
-        // byte/char/short/int/long
-        try {
-            reader.readValue("{\"byteValue\":null}");
-            fail("Expected failure for byte + null");
-        } catch (JsonMappingException e) {
-            verifyException(e, "Can not map JSON null into type byte");
-        }
-        try {
-            reader.readValue("{\"charValue\":null}");
-            fail("Expected failure for char + null");
-        } catch (JsonMappingException e) {
-            verifyException(e, "Can not map JSON null into type char");
-        }
-        try {
-            reader.readValue("{\"shortValue\":null}");
-            fail("Expected failure for short + null");
-        } catch (JsonMappingException e) {
-            verifyException(e, "Can not map JSON null into type short");
-        }
-        try {
-            reader.readValue("{\"intValue\":null}");
-            fail("Expected failure for int + null");
-        } catch (JsonMappingException e) {
-            verifyException(e, "Can not map JSON null into type int");
-        }
-        try {
-            reader.readValue("{\"longValue\":null}");
-            fail("Expected failure for long + null");
-        } catch (JsonMappingException e) {
-            verifyException(e, "Can not map JSON null into type long");
-        }
-
-        // float/double
-        try {
-            reader.readValue("{\"floatValue\":null}");
-            fail("Expected failure for float + null");
-        } catch (JsonMappingException e) {
-            verifyException(e, "Can not map JSON null into type float");
-        }
-        try {
-            reader.readValue("{\"doubleValue\":null}");
-            fail("Expected failure for double + null");
-        } catch (JsonMappingException e) {
-            verifyException(e, "Can not map JSON null into type double");
-        }
-    }
-    
     // [databind#407]
     public void testListOfNulls() throws Exception
     {
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/PropertyAliasTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/PropertyAliasTest.java
new file mode 100644
index 0000000..3cc17ff
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/PropertyAliasTest.java
@@ -0,0 +1,84 @@
+package com.fasterxml.jackson.databind.deser;
+
+import com.fasterxml.jackson.annotation.JsonAlias;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonSubTypes;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class PropertyAliasTest extends BaseMapTest
+{
+    static class AliasBean {
+        @JsonAlias({ "nm", "Name" })
+        public String name;
+
+        int _xyz;
+
+        int _a;
+
+        @JsonCreator
+        public AliasBean(@JsonProperty("a")
+            @JsonAlias("A") int a) {
+            _a = a;
+        }
+        
+        @JsonAlias({ "Xyz" })
+        public void setXyz(int x) {
+            _xyz = x;
+        }
+    }
+
+    static class PolyWrapperForAlias {
+        @JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
+                include = JsonTypeInfo.As.WRAPPER_ARRAY)
+        @JsonSubTypes({
+            @JsonSubTypes.Type(value = AliasBean.class,name = "ab"),
+        })
+        public Object value;
+
+        protected PolyWrapperForAlias() { }
+        public PolyWrapperForAlias(Object v) { value = v; }
+    }
+
+    private final ObjectMapper MAPPER = newObjectMapper();
+
+    // [databind#1029]
+    public void testSimpleAliases() throws Exception
+    {
+        AliasBean bean;
+
+        // first, one indicated by field annotation, set via field
+        bean = MAPPER.readValue(aposToQuotes("{'Name':'Foobar','a':3,'xyz':37}"),
+                AliasBean.class);
+        assertEquals("Foobar", bean.name);
+        assertEquals(3, bean._a);
+        assertEquals(37, bean._xyz);
+
+        // then method-bound one
+        bean = MAPPER.readValue(aposToQuotes("{'name':'Foobar','a':3,'Xyz':37}"),
+                AliasBean.class);
+        assertEquals("Foobar", bean.name);
+        assertEquals(3, bean._a);
+        assertEquals(37, bean._xyz);
+        
+        // and finally, constructor-backed one
+        bean = MAPPER.readValue(aposToQuotes("{'name':'Foobar','A':3,'xyz':37}"),
+                AliasBean.class);
+        assertEquals("Foobar", bean.name);
+        assertEquals(3, bean._a);
+        assertEquals(37, bean._xyz);
+    }
+
+    public void testAliasWithPolymorphic() throws Exception
+    {
+        PolyWrapperForAlias value = MAPPER.readValue(aposToQuotes(
+                "{'value': ['ab', {'nm' : 'Bob', 'A' : 17} ] }"
+                ), PolyWrapperForAlias.class);
+        assertNotNull(value.value);
+        AliasBean bean = (AliasBean) value.value;
+        assertEquals("Bob", bean.name);
+        assertEquals(17, bean._a);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/ReadOnlyDeser1805Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/ReadOnlyDeser1805Test.java
new file mode 100644
index 0000000..a8f68d0
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/ReadOnlyDeser1805Test.java
@@ -0,0 +1,78 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.*;
+
+public class ReadOnlyDeser1805Test extends BaseMapTest
+{
+    static class Foo {
+        @JsonProperty(access = JsonProperty.Access.READ_ONLY)
+        private List<Long> list = new ArrayList<>();
+
+        List<Long> getList() {
+            return list;
+        }
+
+        public Foo setList(List<Long> list) {
+            this.list = list;
+            return this;
+        }
+    }
+
+    // [databind#1805]
+    static class UserWithReadOnly {
+        public String name;
+
+        @JsonProperty(access = JsonProperty.Access.READ_ONLY)
+        public List<String> getRoles() {
+            return Arrays.asList("admin", "monitor");
+        }
+    }
+
+    // [databind#1805]
+    @JsonIgnoreProperties(value={ "roles" }, allowGetters=true)
+    static class UserAllowGetters {
+        public String name;
+
+        public List<String> getRoles() {
+            return Arrays.asList("admin", "monitor");
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = newObjectMapper();
+
+    public void testReadOnly1382() throws Exception
+    {
+        String payload = "{\"list\":[1,2,3,4]}";
+        Foo foo = MAPPER.readValue(payload, Foo.class);
+        assertTrue("List should be empty", foo.getList().isEmpty());
+    }
+
+    // [databind#1805]
+    public void testViaReadOnly() throws Exception {
+        UserWithReadOnly user = new UserWithReadOnly();
+        user.name = "foo";
+        String json = MAPPER.writeValueAsString(user);
+        UserWithReadOnly result = MAPPER.readValue(json, UserWithReadOnly.class);
+        assertNotNull(result);
+    }
+
+    // [databind#1805]
+    public void testUsingAllowGetters() throws Exception {
+        UserAllowGetters user = new UserAllowGetters();
+        user.name = "foo";
+        String json = MAPPER.writeValueAsString(user);
+        assertTrue(json.contains("roles"));
+        UserAllowGetters result = MAPPER.readValue(json, UserAllowGetters.class);
+        assertNotNull(result);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestAbstract.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestAbstract.java
deleted file mode 100644
index 28830fe..0000000
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestAbstract.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package com.fasterxml.jackson.databind.deser;
-
-
-import com.fasterxml.jackson.core.*;
-import com.fasterxml.jackson.databind.*;
-
-/**
- * Tests for checking handling of abstract types.
- */
-public class TestAbstract
-    extends BaseMapTest
-{
-    static abstract class Abstract {
-        public int x;
-    }
-    
-    /*
-    /**********************************************************
-    /* Unit tests
-    /**********************************************************
-     */
-    
-    /**
-     * Test to verify details of how trying to deserialize into
-     * abstract type should fail (if there is no way to determine
-     * actual type information for the concrete type to use)
-     */
-    public void testAbstractFailure() throws Exception
-    {
-        ObjectMapper m = new ObjectMapper();
-        try {
-            m.readValue("{ \"x\" : 3 }", Abstract.class);
-            fail("Should fail on trying to deserialize abstract type");
-        } catch (JsonProcessingException e) {
-            verifyException(e, "can not construct");
-        }
-    }
-}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestBasicAnnotations.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestBasicAnnotations.java
index 1240bd3..4796e40 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestBasicAnnotations.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestBasicAnnotations.java
@@ -3,7 +3,7 @@
 import java.io.*;
 
 import com.fasterxml.jackson.annotation.*;
-
+import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
 import com.fasterxml.jackson.core.*;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
@@ -18,12 +18,6 @@
 public class TestBasicAnnotations
     extends BaseMapTest
 {
-    /*
-    /**********************************************************
-    /* Annotated helper classes
-    /**********************************************************
-     */
-
     /// Class for testing {@link JsonProperty} annotations
     final static class SizeClassSetter
     {
@@ -88,6 +82,24 @@
         @JsonDeserialize protected int a;
     }
 
+    @JsonAutoDetect(setterVisibility=Visibility.NONE)
+    final static class Dummy { }
+
+    final static class EmptyDummy { }
+
+    static class AnnoBean {
+        int value = 3;
+        
+        @JsonProperty("y")
+        public void setX(int v) { value = v; }
+    }
+
+    enum Alpha { A, B, C; }
+
+    public static class SimpleBean {
+        public int x, y;
+    }
+
     /*
     /**********************************************************
     /* Other helper classes
@@ -104,10 +116,10 @@
             return new int[] { jp.getIntValue() };
         }
     }
-    
+
     /*
     /**********************************************************
-    /* Test methods
+    /* Test methods, basic
     /**********************************************************
      */
 
@@ -168,4 +180,45 @@
         Issue442Bean bean = MAPPER.readValue("{\"i\":5}", Issue442Bean.class);
         assertEquals(5, bean.w.i);
     }
+
+    /*
+    /**********************************************************
+    /* Test methods, annotations disabled
+    /**********************************************************
+     */
+
+    public void testAnnotationsDisabled() throws Exception
+    {
+        // first: verify that annotation introspection is enabled by default
+        assertTrue(MAPPER.getDeserializationConfig().isEnabled(MapperFeature.USE_ANNOTATIONS));
+        // with annotations, property is renamed
+        AnnoBean bean = MAPPER.readValue("{ \"y\" : 0 }", AnnoBean.class);
+        assertEquals(0, bean.value);
+
+        ObjectMapper m = new ObjectMapper();
+        m.configure(MapperFeature.USE_ANNOTATIONS, false);
+        // without annotations, should default to default bean-based name...
+        bean = m.readValue("{ \"x\" : 0 }", AnnoBean.class);
+        assertEquals(0, bean.value);
+    }
+
+    public void testEnumsWhenDisabled() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        assertEquals(Alpha.B, m.readValue(quote("B"), Alpha.class));
+
+        m = new ObjectMapper();
+        m.configure(MapperFeature.USE_ANNOTATIONS, false);
+        // should still use the basic name handling here
+        assertEquals(Alpha.B, m.readValue(quote("B"), Alpha.class));
+    }
+
+    public void testNoAccessOverrides() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.disable(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS);
+        SimpleBean bean = m.readValue("{\"x\":1,\"y\":2}", SimpleBean.class);
+        assertEquals(1, bean.x);
+        assertEquals(2, bean.y);
+    }    
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestBeanDeserializer.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestBeanDeserializer.java
index cd1a506..988ece3 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestBeanDeserializer.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestBeanDeserializer.java
@@ -3,11 +3,10 @@
 import java.io.IOException;
 import java.util.*;
 
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.core.*;
 import com.fasterxml.jackson.databind.*;
-import com.fasterxml.jackson.databind.deser.BeanDeserializer;
-import com.fasterxml.jackson.databind.deser.BeanDeserializerBuilder;
-import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
 import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
 import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer;
 import com.fasterxml.jackson.databind.module.SimpleModule;
@@ -18,11 +17,9 @@
 @SuppressWarnings("serial")
 public class TestBeanDeserializer extends BaseMapTest
 {
-    /*
-    /**********************************************************
-    /* Helper types
-    /**********************************************************
-     */
+    static abstract class Abstract {
+        public int x;
+    }
 
     static class Bean {
         public String b = "b";
@@ -141,7 +138,92 @@
             context.addBeanDeserializerModifier(new Issue476DeserializerModifier());
         }        
     }
-    
+
+    public static class Issue1912Bean {
+        public Issue1912SubBean subBean;
+
+        @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) // This is need to populate _propertyBasedCreator on BeanDeserializerBase
+        public Issue1912Bean(@JsonProperty("subBean") Issue1912SubBean subBean) {
+            this.subBean = subBean;
+        }
+    }
+    public static class Issue1912SubBean {
+        public String a;
+
+        public Issue1912SubBean() { }
+
+        public Issue1912SubBean(String a) {
+            this.a = a;
+        }
+    }
+
+    public static class Issue1912CustomBeanDeserializer extends JsonDeserializer<Issue1912Bean> {
+        private BeanDeserializer defaultDeserializer;
+
+        public Issue1912CustomBeanDeserializer(BeanDeserializer defaultDeserializer) {
+            this.defaultDeserializer = defaultDeserializer;
+        }
+
+        @Override
+        public Issue1912Bean deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
+            // this is need on some cases, this populate _propertyBasedCreator
+            defaultDeserializer.resolve(ctxt);
+
+            p.nextFieldName(); // read subBean
+            p.nextToken(); // read start object
+
+            Issue1912SubBean subBean = (Issue1912SubBean) defaultDeserializer.findProperty("subBean").deserialize(p, ctxt);
+
+            return new Issue1912Bean(subBean);
+        }
+    }
+
+    public static class Issue1912CustomPropertyDeserializer extends JsonDeserializer<Issue1912SubBean> {
+
+        @Override
+        public Issue1912SubBean deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
+            p.nextFieldName(); // read "a"
+            Issue1912SubBean object = new Issue1912SubBean(p.nextTextValue() + "_custom");
+            p.nextToken();
+            return object;
+        }
+    }
+    public static class Issue1912UseAddOrReplacePropertyDeserializerModifier extends BeanDeserializerModifier {
+
+        @Override
+        public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
+            if (beanDesc.getBeanClass() == Issue1912Bean.class) {
+                return new Issue1912CustomBeanDeserializer((BeanDeserializer) deserializer);
+            }
+            return super.modifyDeserializer(config, beanDesc, deserializer);
+        }
+
+        @Override
+        public BeanDeserializerBuilder updateBuilder(DeserializationConfig config, BeanDescription beanDesc, BeanDeserializerBuilder builder) {
+            if (beanDesc.getBeanClass() == Issue1912Bean.class) {
+                Iterator<SettableBeanProperty> props = builder.getProperties();
+                while (props.hasNext()) {
+                    SettableBeanProperty prop = props.next();
+                    SettableBeanProperty propWithCustomDeserializer = prop.withValueDeserializer(new Issue1912CustomPropertyDeserializer());
+                    builder.addOrReplaceProperty(propWithCustomDeserializer, true);
+                }
+            }
+
+            return builder;
+        }
+    }
+    public class Issue1912Module extends SimpleModule {
+
+        public Issue1912Module() {
+            super("Issue1912Module", Version.unknownVersion());
+        }
+
+        @Override
+        public void setupModule(SetupContext context) {
+            context.addBeanDeserializerModifier(new Issue1912UseAddOrReplacePropertyDeserializerModifier());
+        }
+    }
+
     // [Issue#121], arrays, collections, maps
 
     enum EnumABC { A, B, C; }
@@ -242,6 +324,20 @@
 
     private final ObjectMapper MAPPER = new ObjectMapper();
 
+    /**
+     * Test to verify details of how trying to deserialize into
+     * abstract type should fail (if there is no way to determine
+     * actual type information for the concrete type to use)
+     */
+    public void testAbstractFailure() throws Exception
+    {
+        try {
+            MAPPER.readValue("{ \"x\" : 3 }", Abstract.class);
+            fail("Should fail on trying to deserialize abstract type");
+        } catch (JsonProcessingException e) {
+            verifyException(e, "cannot construct");
+        }
+    }    
     public void testPropertyRemoval() throws Exception
     {
         ObjectMapper mapper = new ObjectMapper();
@@ -368,4 +464,11 @@
         assertEquals("ABCDEF", result);
     }
 
+    public void testAddOrReplacePropertyIsUsedOnDeserialization() throws Exception {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(new Issue1912Module());
+
+        Issue1912Bean result = mapper.readValue("{\"subBean\": {\"a\":\"foo\"}}", Issue1912Bean.class);
+        assertEquals("foo_custom", result.subBean.a);
+    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestConfig.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestConfig.java
deleted file mode 100644
index 6b2dbf4..0000000
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestConfig.java
+++ /dev/null
@@ -1,120 +0,0 @@
-package com.fasterxml.jackson.databind.deser;
-
-import com.fasterxml.jackson.annotation.*;
-import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
-
-import com.fasterxml.jackson.databind.*;
-import com.fasterxml.jackson.databind.introspect.ClassIntrospector;
-
-/**
- * Unit tests for checking handling of DeserializationConfig.
- */
-public class TestConfig
-    extends BaseMapTest
-{
-    @JsonAutoDetect(setterVisibility=Visibility.NONE)
-    final static class Dummy { }
-
-    final static class EmptyDummy { }
-
-    static class AnnoBean {
-        int value = 3;
-        
-        @JsonProperty("y")
-            public void setX(int v) { value = v; }
-    }
-
-    enum Alpha { A, B, C; }
-
-    public static class SimpleBean {
-        public int x, y;
-    }
-    
-    /*
-    /**********************************************************
-    /* Main tests
-    /**********************************************************
-     */
-
-    /* Test to verify that we don't overflow number of features; if we
-     * hit the limit, need to change implementation -- this test just
-     * gives low-water mark
-     */
-    public void testEnumIndexes()
-    {
-        int max = 0;
-        
-        for (DeserializationFeature f : DeserializationFeature.values()) {
-            max = Math.max(max, f.ordinal());
-        }
-        if (max >= 31) { // 31 is actually ok; 32 not
-            fail("Max number of DeserializationFeature enums reached: "+max);
-        }
-    }
-    
-    public void testDefaults()
-    {
-        ObjectMapper m = new ObjectMapper();
-        DeserializationConfig cfg = m.getDeserializationConfig();
-
-        // Expected defaults:
-        assertTrue(cfg.isEnabled(MapperFeature.USE_ANNOTATIONS));
-        assertTrue(cfg.isEnabled(MapperFeature.AUTO_DETECT_SETTERS));
-        assertTrue(cfg.isEnabled(MapperFeature.AUTO_DETECT_CREATORS));
-        assertTrue(cfg.isEnabled(MapperFeature.USE_GETTERS_AS_SETTERS));
-        assertTrue(cfg.isEnabled(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS));
-
-
-        assertFalse(cfg.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS));
-        assertFalse(cfg.isEnabled(DeserializationFeature.USE_BIG_INTEGER_FOR_INTS));
-
-        assertTrue(cfg.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));
-    }
-
-    public void testOverrideIntrospectors()
-    {
-        ObjectMapper m = new ObjectMapper();
-        DeserializationConfig cfg = m.getDeserializationConfig();
-        // and finally, ensure we could override introspectors
-        cfg = cfg.with((ClassIntrospector) null); // no way to verify tho
-        cfg = cfg.with((AnnotationIntrospector) null);
-        assertNull(cfg.getAnnotationIntrospector());
-    }
-        
-    public void testAnnotationsDisabled() throws Exception
-    {
-        // first: verify that annotation introspection is enabled by default
-        ObjectMapper m = new ObjectMapper();
-        assertTrue(m.getDeserializationConfig().isEnabled(MapperFeature.USE_ANNOTATIONS));
-        // with annotations, property is renamed
-        AnnoBean bean = m.readValue("{ \"y\" : 0 }", AnnoBean.class);
-        assertEquals(0, bean.value);
-
-        m = new ObjectMapper();
-        m.configure(MapperFeature.USE_ANNOTATIONS, false);
-        // without annotations, should default to default bean-based name...
-        bean = m.readValue("{ \"x\" : 0 }", AnnoBean.class);
-        assertEquals(0, bean.value);
-    }
-
-    // [JACKSON-875]
-    public void testEnumsWhenDisabled() throws Exception
-    {
-        ObjectMapper m = new ObjectMapper();
-        assertEquals(Alpha.B, m.readValue(quote("B"), Alpha.class));
-
-        m = new ObjectMapper();
-        m.configure(MapperFeature.USE_ANNOTATIONS, false);
-        // should still use the basic name handling here
-        assertEquals(Alpha.B, m.readValue(quote("B"), Alpha.class));
-    }
-
-    public void testNoAccessOverrides() throws Exception
-    {
-        ObjectMapper m = new ObjectMapper();
-        m.disable(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS);
-        SimpleBean bean = m.readValue("{\"x\":1,\"y\":2}", SimpleBean.class);
-        assertEquals(1, bean.x);
-        assertEquals(2, bean.y);
-    }
-}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestCustomDeserializers.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestCustomDeserializers.java
index 58c5251..f85d995 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestCustomDeserializers.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestCustomDeserializers.java
@@ -6,7 +6,9 @@
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
+
 import com.fasterxml.jackson.core.*;
+
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.annotation.*;
 import com.fasterxml.jackson.databind.deser.std.*;
@@ -37,11 +39,11 @@
         }
 
         @Override
-        public T deserialize(JsonParser jp, DeserializationContext ctxt)
-            throws IOException, JsonProcessingException
+        public T deserialize(JsonParser p, DeserializationContext ctxt)
+            throws IOException
         {
             // need to skip, if structured...
-            jp.skipChildren();
+            p.skipChildren();
             return value;
         }
     }
@@ -65,29 +67,29 @@
     static class CustomBeanDeserializer extends JsonDeserializer<CustomBean>
     {
         @Override
-        public CustomBean deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException
+        public CustomBean deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
         {
             int a = 0, b = 0;
-            JsonToken t = jp.getCurrentToken();
+            JsonToken t = p.getCurrentToken();
             if (t == JsonToken.START_OBJECT) {
-                t = jp.nextToken();
+                t = p.nextToken();
             } else if (t != JsonToken.FIELD_NAME) {
                 throw new Error();
             }
             while(t == JsonToken.FIELD_NAME) {
-                final String fieldName = jp.getCurrentName();
-                t = jp.nextToken();
+                final String fieldName = p.getCurrentName();
+                t = p.nextToken();
                 if (t != JsonToken.VALUE_NUMBER_INT) {
-                    throw new JsonParseException(jp, "expecting number got "+ t);
+                    throw new JsonParseException(p, "expecting number got "+ t);
                 }
                 if (fieldName.equals("a")) {
-                    a = jp.getIntValue();
+                    a = p.getIntValue();
                 } else if (fieldName.equals("b")) {
-                    b = jp.getIntValue();
+                    b = p.getIntValue();
                 } else {
                     throw new Error();
                 }
-                t = jp.nextToken();
+                t = p.nextToken();
             }
             return new CustomBean(a, b);
         }
@@ -102,7 +104,6 @@
         }
     }
 
-    // [JACKSON-882]
     public static class CustomKey {
         private final int id;
 
@@ -130,8 +131,8 @@
      
     static class CustomKeySerializer extends JsonSerializer<CustomKey> {
         @Override
-        public void serialize(CustomKey value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
-            jgen.writeFieldName(String.valueOf(value.getId()));
+        public void serialize(CustomKey value, JsonGenerator g, SerializerProvider provider) throws IOException {
+            g.writeFieldName(String.valueOf(value.getId()));
         }
     }
 
@@ -142,7 +143,7 @@
         }
     }
 
-    // [#375]
+    // [databind#375]
 
     @Target({ElementType.FIELD})
     @Retention(RetentionPolicy.RUNTIME)
@@ -201,9 +202,9 @@
         }
 
         @Override
-        public Bean375Inner deserialize(JsonParser jp, DeserializationContext ctxt)
+        public Bean375Inner deserialize(JsonParser p, DeserializationContext ctxt)
                 throws IOException, JsonProcessingException {
-            int x = jp.getIntValue();
+            int x = p.getIntValue();
             if (negative) {
                 x = -x;
             } else {
@@ -257,7 +258,55 @@
             return p.getText().toUpperCase();
         }
     }
-    
+
+    static class DelegatingModuleImpl extends SimpleModule
+    {
+        public DelegatingModuleImpl() {
+            super("test", Version.unknownVersion());
+        }
+
+        @Override
+        public void setupModule(SetupContext context)
+        {
+            super.setupModule(context);
+            context.addBeanDeserializerModifier(new BeanDeserializerModifier() {
+                @Override
+                public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config,
+                        BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
+                    if (deserializer.handledType() == String.class) {
+                        JsonDeserializer<?> d = new MyStringDeserializer(deserializer);
+                        // just for test coverage purposes...
+                        if (d.getDelegatee() != deserializer) {
+                            throw new Error("Cannot access delegatee!");
+                        }
+                        return d;
+                    }
+                    return deserializer;
+                }
+            });
+        }
+    }
+
+    static class MyStringDeserializer extends DelegatingDeserializer
+    {
+        public MyStringDeserializer(JsonDeserializer<?> newDel) {
+            super(newDel);
+        }
+
+        @Override
+        protected JsonDeserializer<?> newDelegatingInstance(JsonDeserializer<?> newDel) {
+            return new MyStringDeserializer(newDel);
+        }
+
+        @Override
+        public Object deserialize(JsonParser p, DeserializationContext ctxt)
+            throws IOException
+        {
+            Object ob = _delegatee.deserialize(p, ctxt);
+            return "MY:"+ob;
+        }
+    }
+
     /*
     /**********************************************************
     /* Unit tests
@@ -399,4 +448,12 @@
         assertNotNull(sw);
         assertEquals("FOO", sw.str);
     }
+
+    public void testDelegatingDeserializer() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper().registerModule(
+                new DelegatingModuleImpl());
+        String str = mapper.readValue(quote("foo"), String.class);
+        assertEquals("MY:foo", str);
+    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestGenericCollectionDeser.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestGenericCollectionDeser.java
index f99bbb2..a87670e 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestGenericCollectionDeser.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestGenericCollectionDeser.java
@@ -2,6 +2,8 @@
 
 import java.util.*;
 
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
 
@@ -9,12 +11,6 @@
 public class TestGenericCollectionDeser
     extends BaseMapTest
 {
-    /*
-    /**********************************************************
-    /* Test classes, enums
-    /**********************************************************
-     */
-
     static class ListSubClass extends ArrayList<StringWrapper> { }
 
     /**
@@ -24,15 +20,18 @@
     @JsonDeserialize(contentAs=StringWrapper.class)
     static class AnnotatedStringList extends ArrayList<Object> { }
 
-    @JsonDeserialize(contentAs=BooleanWrapper.class)
+    @JsonDeserialize(contentAs=BooleanElement.class)
     static class AnnotatedBooleanList extends ArrayList<Object> { }
 
-    /*
-    /**********************************************************
-    /* Test methods
-    /**********************************************************
-     */
+    protected static class BooleanElement {
+        public Boolean b;
 
+        @JsonCreator
+        public BooleanElement(Boolean value) { b = value; }
+
+        @JsonValue public Boolean value() { return b; }
+    }
+    
     /*
     /**********************************************************
     /* Tests for sub-classing
@@ -76,7 +75,7 @@
         AnnotatedBooleanList result = mapper.readValue("[ false ]", AnnotatedBooleanList.class);
         assertEquals(1, result.size());
         Object ob = result.get(0);
-        assertEquals(BooleanWrapper.class, ob.getClass());
-        assertFalse(((BooleanWrapper) ob).b);
+        assertEquals(BooleanElement.class, ob.getClass());
+        assertFalse(((BooleanElement) ob).b);
     }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestInnerClass.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestInnerClass.java
index 4be0a63..556e996 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestInnerClass.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestInnerClass.java
@@ -1,10 +1,10 @@
 package com.fasterxml.jackson.databind.deser;
 
+import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.databind.*;
 
 public class TestInnerClass extends BaseMapTest
 {
-    // [JACKSON-594]
     static class Dog
     {
       public String name;
@@ -16,9 +16,10 @@
           brain = new Brain();
           brain.isThinking = thinking;
       }
-      
+
       // note: non-static
       public class Brain {
+          @JsonProperty("brainiac")
           public boolean isThinking;
 
           public String parentName() { return name; }
@@ -45,5 +46,11 @@
         assertEquals("Smurf", output.brain.parentName());
         output.name = "Foo";
         assertEquals("Foo", output.brain.parentName());
+
+        // also, null handling
+        input.brain = null;
+
+        output = mapper.readValue(mapper.writeValueAsString(input), Dog.class);
+        assertNull(output.brain);
     }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderAdvancedTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderAdvancedTest.java
new file mode 100644
index 0000000..e3f2644
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderAdvancedTest.java
@@ -0,0 +1,63 @@
+package com.fasterxml.jackson.databind.deser.builder;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+
+public class BuilderAdvancedTest extends BaseMapTest
+{
+    @JsonDeserialize(builder=InjectableBuilderXY.class)
+    static class InjectableXY
+    {
+        final int _x, _y;
+        final String _stuff;
+
+        protected InjectableXY(int x, int y, String stuff) {
+            _x = x+1;
+            _y = y+1;
+            _stuff = stuff;
+        }
+    }
+
+    static class InjectableBuilderXY
+    {
+        public int x, y;
+
+        @JacksonInject
+        protected String stuff;
+        
+        public InjectableBuilderXY withX(int x0) {
+              this.x = x0;
+              return this;
+        }
+
+        public InjectableBuilderXY withY(int y0) {
+              this.y = y0;
+              return this;
+        }
+
+        public InjectableXY build() {
+              return new InjectableXY(x, y, stuff);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+    
+    public void testWithInjectable() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.setInjectableValues(new InjectableValues.Std()
+            .addValue(String.class, "stuffValue")
+            );
+        InjectableXY bean = mapper.readValue(aposToQuotes("{'y':3,'x':7}"),
+                InjectableXY.class);
+        assertEquals(8, bean._x);
+        assertEquals(4, bean._y);
+        assertEquals("stuffValue", bean._stuff);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderErrorHandling.java b/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderErrorHandling.java
new file mode 100644
index 0000000..f7f67b0
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderErrorHandling.java
@@ -0,0 +1,66 @@
+package com.fasterxml.jackson.databind.deser.builder;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.exc.MismatchedInputException;
+
+public class BuilderErrorHandling extends BaseMapTest
+{
+    @JsonDeserialize(builder=SimpleBuilderXY.class)
+    static class ValueClassXY
+    {
+        final int _x, _y;
+
+        protected ValueClassXY(int x, int y) {
+            _x = x+1;
+            _y = y+1;
+        }
+    }
+
+    static class SimpleBuilderXY
+    {
+        int x, y;
+     
+        public SimpleBuilderXY withX(int x0) {
+              this.x = x0;
+              return this;
+        }
+
+        public SimpleBuilderXY withY(int y0) {
+              this.y = y0;
+              return this;
+        }
+
+        public ValueClassXY build() {
+              return new ValueClassXY(x, y);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+    
+    public void testUnknownProperty() throws Exception
+    {
+        // first, default failure
+        String json = aposToQuotes("{'x':1,'z':2,'y':4}");
+        try {
+            MAPPER.readValue(json, ValueClassXY.class);
+            fail("Should not pass");
+        } catch (MismatchedInputException e) {
+            verifyException(e, "unrecognized field");
+        }
+        // but pass if ok to ignore
+        ValueClassXY result = MAPPER.readerFor(ValueClassXY.class)
+                .without(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
+                .readValue(json);
+        assertEquals(2, result._x);
+        assertEquals(5, result._y);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderFailTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderFailTest.java
new file mode 100644
index 0000000..b674a89
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderFailTest.java
@@ -0,0 +1,88 @@
+package com.fasterxml.jackson.databind.deser.builder;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
+import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
+
+public class BuilderFailTest extends BaseMapTest
+{
+    @JsonDeserialize(builder=SimpleBuilderXY.class)
+    static class ValueClassXY
+    {
+        final int _x, _y;
+
+        protected ValueClassXY(int x, int y) {
+            _x = x+1;
+            _y = y+1;
+        }
+    }
+
+    static class SimpleBuilderXY
+    {
+        public int x, y;
+     
+        public SimpleBuilderXY withX(int x0) {
+              this.x = x0;
+              return this;
+        }
+
+        public SimpleBuilderXY withY(int y0) {
+              this.y = y0;
+              return this;
+        }
+
+        public ValueClassXY build() {
+              return new ValueClassXY(x, y);
+        }
+    }
+
+    // for [databind#761]
+    @JsonDeserialize(builder = ValueBuilderWrongBuildType.class)
+    static class ValueClassWrongBuildType {
+    }
+
+    static class ValueBuilderWrongBuildType
+    {
+        public int x;
+
+        public ValueBuilderWrongBuildType withX(int x0) {
+            this.x = x0;
+            return this;
+        }
+
+        public ValueClassXY build() {
+            return null;
+        }
+    }
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+
+    public void testBuilderMethodReturnInvalidType() throws Exception
+    {
+        final String json = "{\"x\":1}";
+        try {
+            MAPPER.readValue(json, ValueClassWrongBuildType.class);
+            fail("Missing expected JsonProcessingException exception");
+        } catch (InvalidDefinitionException e) {
+            verifyException(e, "Build method");
+            verifyException(e, "has wrong return type");
+        }
+    }
+
+    public void testExtraFields() throws Exception
+    {
+        final String json = aposToQuotes("{'x':1,'y':2,'z':3}");
+        try {
+            MAPPER.readValue(json, ValueClassXY.class);
+            fail("should not pass");
+        } catch (UnrecognizedPropertyException e) {
+            verifyException(e, "Unrecognized field \"z\"");
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderInfiniteLoop1978Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderInfiniteLoop1978Test.java
new file mode 100644
index 0000000..0a6a067
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderInfiniteLoop1978Test.java
@@ -0,0 +1,95 @@
+package com.fasterxml.jackson.databind.deser.builder;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonUnwrapped;
+
+import com.fasterxml.jackson.databind.*;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+
+public class BuilderInfiniteLoop1978Test extends BaseMapTest
+{
+    static class Builder
+    {
+         private SubBean temp;
+         private int id;
+
+         Builder(@JsonProperty("beanId") int beanId) {
+              this.id = beanId;
+         }
+
+         @JsonUnwrapped(prefix="sub.")
+         public Builder withThing(SubBean thing) {
+             this.temp = thing;
+             return this;
+         }
+
+         public Bean build()
+         {
+             Bean bean = new Bean(id);
+             bean.setThing( temp );
+             return bean;
+         }
+    }
+
+    @JsonDeserialize(builder = Builder.class)
+    static class Bean
+    {
+        int id;
+        SubBean thing;
+
+        public Bean(int id) {
+            this.id = id;
+        }
+
+        public SubBean getThing() {
+            return thing;
+        }
+
+        public void setThing( SubBean thing ) {
+            this.thing = thing;
+        }
+    }
+
+    static class SubBuilder
+    {
+         private int element1;
+         private String element2;
+         
+         @JsonProperty("el1")
+         public SubBuilder withElement1(int e1) {
+              this.element1 = e1;
+              return this;
+         }
+         
+         public SubBean build()
+         {
+              SubBean bean = new SubBean();
+              bean.element1 = element1;
+              bean.element2 = element2;
+              return bean;
+         }
+    }
+
+    @JsonDeserialize(builder = SubBuilder.class)
+    static class SubBean
+    {
+        public int element1;
+        public String element2;
+    }
+
+    /*
+    /**********************************************************************
+    /* Test methods
+    /**********************************************************************
+     */
+
+    // for [databind#1978]
+    public void testInfiniteLoop1978() throws Exception
+    {
+        String json = "{\"sub.el1\":34,\"sub.el2\":\"some text\"}";
+        ObjectMapper mapper = new ObjectMapper();
+        Bean bean = mapper.readValue( json, Bean.class );
+        assertNotNull(bean);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/BuilderSimpleTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderSimpleTest.java
similarity index 77%
rename from src/test/java/com/fasterxml/jackson/databind/creators/BuilderSimpleTest.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderSimpleTest.java
index 3ae76e7..7644dc4 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/BuilderSimpleTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderSimpleTest.java
@@ -1,21 +1,21 @@
-package com.fasterxml.jackson.databind.creators;
+package com.fasterxml.jackson.databind.deser.builder;
 
 import java.util.*;
 
-import com.fasterxml.jackson.annotation.JsonAnySetter;
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.annotation.JsonSetter;
-import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.core.Version;
+
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
 import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
+import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector;
 
 public class BuilderSimpleTest extends BaseMapTest
 {
     // // Simple 2-property value class, builder with standard naming
-	
+
     @JsonDeserialize(builder=SimpleBuilderXY.class)
     static class ValueClassXY
     {
@@ -47,7 +47,7 @@
     }
 
     // // 3-property value, with more varied builder
-	
+
     @JsonDeserialize(builder=BuildABC.class)
     static class ValueClassABC
     {
@@ -60,6 +60,7 @@
         }
     }
 
+    @JsonIgnoreProperties({ "d" })
     static class BuildABC
     {
         public int a; // to be used as is
@@ -105,7 +106,6 @@
             return new ValueImmutable(value);
         }
     }
-    
     // And then with custom naming:
 
     @JsonDeserialize(builder=BuildFoo.class)
@@ -128,40 +128,6 @@
         }
     }
 
-    // And with creator(s)
-	
-    @JsonDeserialize(builder=CreatorBuilder.class)
-    static class CreatorValue
-    {
-        final int a, b, c;
-
-        protected CreatorValue(int a, int b, int c) {
-            this.a = a;
-            this.b = b;
-            this.c = c;
-        }
-    }
-
-    static class CreatorBuilder {
-        private final int a, b;
-        private int c;
-
-        @JsonCreator
-        public CreatorBuilder(@JsonProperty("a") int a,
-                @JsonProperty("b") int b)
-        {
-            this.a = a;
-            this.b = b;
-        }
-        
-        public CreatorBuilder withC(int v) {
-            c = v;
-            return this;
-        }
-        public CreatorValue build() {
-            return new CreatorValue(a, b, c);
-        }
-    }
 
     // for [databind#761]
 
@@ -231,25 +197,6 @@
             return new ValueInterface2Impl(x);
         }
     }
-    
-    // for [databind#761]
-    @JsonDeserialize(builder = ValueBuilderWrongBuildType.class)
-    static class ValueClassWrongBuildType {
-    }
-
-    static class ValueBuilderWrongBuildType
-    {
-        public int x;
-
-        public ValueBuilderWrongBuildType withX(int x0) {
-            this.x = x0;
-            return this;
-        }
-
-        public ValueClassXY build() {
-            return null;
-        }
-    }
 
     // [databind#777]
     @JsonDeserialize(builder = SelfBuilder777.class)
@@ -296,6 +243,31 @@
         }
     }
 
+    protected static class NopModule1557 extends com.fasterxml.jackson.databind.Module
+    {
+        @Override
+        public String getModuleName() {
+            return "NopModule";
+        }
+
+        @Override
+        public Version version() {
+            return Version.unknownVersion();
+        }
+
+        @Override
+        public void setupModule(SetupContext setupContext) {
+            // This annotation introspector has no opinion about builders, make sure it doesn't interfere
+            setupContext.insertAnnotationIntrospector(new NopAnnotationIntrospector() {
+                private static final long serialVersionUID = 1L;
+                @Override
+                public Version version() {
+                    return Version.unknownVersion();
+                }
+            });
+        }
+    }
+
     /*
     /**********************************************************
     /* Unit tests
@@ -306,7 +278,7 @@
 
     public void testSimple() throws Exception
     {
-        String json = "{\"x\":1,\"y\":2}";
+        String json = aposToQuotes("{'x':1,'y':2}");
         Object o = MAPPER.readValue(json, ValueClassXY.class);
         assertNotNull(o);
         assertSame(ValueClassXY.class, o.getClass());
@@ -320,13 +292,14 @@
     public void testSimpleWithIgnores() throws Exception
     {
         // 'z' is unknown, and would fail by default:
-        String json = "{\"x\":1,\"y\":2,\"z\":3}";
+        final String json = aposToQuotes("{'x':1,'y':2,'z':4}");
         Object o = null;
 
         try {
             o = MAPPER.readValue(json, ValueClassXY.class);
             fail("Should not pass");
-        } catch (JsonMappingException e) {
+        } catch (UnrecognizedPropertyException e) {
+            assertEquals("z", e.getPropertyName());
             verifyException(e, "Unrecognized field \"z\"");
         }
 
@@ -345,13 +318,19 @@
     
     public void testMultiAccess() throws Exception
     {
-        String json = "{\"c\":3,\"a\":2,\"b\":-9}";
+        String json = aposToQuotes("{'c':3,'a':2,'b':-9}");
         ValueClassABC value = MAPPER.readValue(json, ValueClassABC.class);
         assertNotNull(value);
-    	    // note: ctor adds one to both values
-        assertEquals(value.a, 2);
-        assertEquals(value.b, -9);
-        assertEquals(value.c, 3);
+        assertEquals(2, value.a);
+        assertEquals(-9, value.b);
+        assertEquals(3, value.c);
+
+        // also, since we can ignore some properties:
+        value = MAPPER.readValue(aposToQuotes("{'c':3,'d':5,'b':-9}"), ValueClassABC.class);
+        assertNotNull(value);
+        assertEquals(0, value.a);
+        assertEquals(-9, value.b);
+        assertEquals(3, value.c);
     }
 
     // test for Immutable builder, to ensure return value is used
@@ -370,16 +349,6 @@
         assertEquals(1, value.value);
     }
 
-    // test to ensure @JsonCreator also work
-    public void testWithCreator() throws Exception
-    {
-        final String json = "{\"a\":1,\"c\":3,\"b\":2}";
-        CreatorValue value = MAPPER.readValue(json, CreatorValue.class);        
-        assertEquals(1, value.a);
-        assertEquals(2, value.b);
-        assertEquals(3, value.c);
-    }
-
     // for [databind#761]
     
     public void testBuilderMethodReturnMoreGeneral() throws Exception
@@ -395,19 +364,6 @@
         ValueInterface2 value = MAPPER.readValue(json, ValueInterface2.class);
         assertEquals(2, value.getX());
     }
-    
-    public void testBuilderMethodReturnInvalidType() throws Exception
-    {
-        final String json = "{\"x\":1}";
-        try {
-            MAPPER.readValue(json, ValueClassWrongBuildType.class);
-            fail("Missing expected JsonProcessingException exception");
-        } catch(JsonProcessingException e) {
-            assertTrue(
-                    "Exception cause must be IllegalArgumentException",
-                    e.getCause() instanceof IllegalArgumentException);
-        }
-    }
 
     public void testSelfBuilder777() throws Exception
     {
@@ -432,5 +388,11 @@
         assertTrue(((List<?>) ob).isEmpty());
     }
 
-    
+    public void testPOJOConfigResolution1557() throws Exception
+    {
+        final String json = "{\"value\":1}";
+        MAPPER.registerModule(new NopModule1557());
+        ValueFoo value = MAPPER.readValue(json, ValueFoo.class);
+        assertEquals(1, value.value);
+    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderViaUpdateTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderViaUpdateTest.java
new file mode 100644
index 0000000..f5b63a5
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderViaUpdateTest.java
@@ -0,0 +1,89 @@
+package com.fasterxml.jackson.databind.deser.builder;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
+
+/**
+ * Tests to ensure that use of "updateValue()" will fail with builder-based deserializers.
+ *
+ * @since 2.9
+ */
+public class BuilderViaUpdateTest extends BaseMapTest
+{
+    @JsonDeserialize(builder=SimpleBuilderXY.class)
+    static class ValueClassXY
+    {
+        protected int x, y;
+
+        protected ValueClassXY(int x, int y) {
+            x = x+1;
+            y = y+1;
+        }
+    }
+
+    static class SimpleBuilderXY
+    {
+        public int x, y;
+
+        public SimpleBuilderXY withX(int x0) {
+            this.x = x0;
+            return this;
+        }
+
+        public SimpleBuilderXY withY(int y0) {
+            this.y = y0;
+            return this;
+        }
+
+        public ValueClassXY build() {
+            return new ValueClassXY(x, y);
+        }
+    }
+
+    /*
+    /*****************************************************
+    /* Basic tests, potential (but not current) success cases
+    /*****************************************************
+     */
+
+    private final static ObjectMapper MAPPER = new ObjectMapper();
+
+    // Tests where result value is passed as thing to update
+    public void testBuilderUpdateWithValue() throws Exception
+    {
+        try {
+            /*ValueClassXY value =*/ MAPPER.readerFor(ValueClassXY.class)
+                    .withValueToUpdate(new ValueClassXY(6, 7))
+                    .readValue(aposToQuotes("{'x':1,'y:'2'}"));
+            fail("Should not have passed");
+        } catch (InvalidDefinitionException e) {
+            verifyException(e, "Deserialization of");
+            verifyException(e, "by passing existing instance");
+            verifyException(e, "ValueClassXY");
+        }
+    }
+
+    /*
+    /*****************************************************
+    /* Failing test cases
+    /*****************************************************
+     */
+
+    // and then test to ensure error handling works as expected if attempts
+    // is made to pass builder (API requires value, not builder)
+    public void testBuilderWithWrongType() throws Exception
+    {
+        try {
+            /* Object result =*/ MAPPER.readerFor(ValueClassXY.class)
+                    .withValueToUpdate(new SimpleBuilderXY())
+                    .readValue(aposToQuotes("{'x':1,'y:'2'}"));
+            fail("Should not have passed");
+        } catch (InvalidDefinitionException e) {
+            verifyException(e, "Deserialization of");
+            verifyException(e, "by passing existing Builder");
+            verifyException(e, "SimpleBuilderXY");
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderWithCreatorTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderWithCreatorTest.java
new file mode 100644
index 0000000..fd2e44c
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderWithCreatorTest.java
@@ -0,0 +1,176 @@
+package com.fasterxml.jackson.databind.deser.builder;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+
+public class BuilderWithCreatorTest extends BaseMapTest
+{
+    @JsonDeserialize(builder=PropertyCreatorBuilder.class)
+    static class PropertyCreatorValue
+    {
+        final int a, b, c;
+
+        protected PropertyCreatorValue(int a, int b, int c) {
+            this.a = a;
+            this.b = b;
+            this.c = c;
+        }
+    }
+
+    static class PropertyCreatorBuilder {
+        private final int a, b;
+        private int c;
+
+        @JsonCreator
+        public PropertyCreatorBuilder(@JsonProperty("a") int a,
+                @JsonProperty("b") int b)
+        {
+            this.a = a;
+            this.b = b;
+        }
+        
+        public PropertyCreatorBuilder withC(int v) {
+            c = v;
+            return this;
+        }
+        public PropertyCreatorValue build() {
+            return new PropertyCreatorValue(a, b, c);
+        }
+    }
+
+    // With String
+    
+    @JsonDeserialize(builder=StringCreatorBuilder.class)
+    static class StringCreatorValue
+    {
+        final String str;
+
+        protected StringCreatorValue(String s) { str = s; }
+    }
+
+    static class StringCreatorBuilder {
+        private final String v;
+
+        @JsonCreator
+        public StringCreatorBuilder(String str) {
+            v = str;
+        }
+
+        public StringCreatorValue build() {
+            return new StringCreatorValue(v);
+        }
+    }
+
+    // With boolean
+    
+    @JsonDeserialize(builder=BooleanCreatorBuilder.class)
+    static class BooleanCreatorValue
+    {
+        final boolean value;
+
+        protected BooleanCreatorValue(boolean v) { value = v; }
+    }
+
+    static class BooleanCreatorBuilder {
+        private final boolean value;
+
+        @JsonCreator
+        public BooleanCreatorBuilder(boolean v) {
+            value = v;
+        }
+
+        public BooleanCreatorValue build() {
+            return new BooleanCreatorValue(value);
+        }
+    }
+    
+    // With Int
+    
+    @JsonDeserialize(builder=IntCreatorBuilder.class)
+    static class IntCreatorValue
+    {
+        final int value;
+
+        protected IntCreatorValue(int v) { value = v; }
+    }
+
+    static class IntCreatorBuilder {
+        private final int value;
+
+        @JsonCreator
+        public IntCreatorBuilder(int v) {
+            value = v;
+        }
+
+        public IntCreatorValue build() {
+            return new IntCreatorValue(value);
+        }
+    }
+
+    // With Double
+    
+    @JsonDeserialize(builder=DoubleCreatorBuilder.class)
+    static class DoubleCreatorValue
+    {
+        final double value;
+
+        protected DoubleCreatorValue(double v) { value = v; }
+    }
+
+    static class DoubleCreatorBuilder {
+        private final double value;
+
+        @JsonCreator
+        public DoubleCreatorBuilder(double v) {
+            value = v;
+        }
+
+        public DoubleCreatorValue build() {
+            return new DoubleCreatorValue(value);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+
+    public void testWithPropertiesCreator() throws Exception
+    {
+        final String json = aposToQuotes("{'a':1,'c':3,'b':2}");
+        PropertyCreatorValue value = MAPPER.readValue(json, PropertyCreatorValue.class);        
+        assertEquals(1, value.a);
+        assertEquals(2, value.b);
+        assertEquals(3, value.c);
+    }
+
+    public void testWithDelegatingStringCreator() throws Exception
+    {
+        final int EXP = 139;
+        IntCreatorValue value = MAPPER.readValue(String.valueOf(EXP),
+                IntCreatorValue.class);        
+        assertEquals(EXP, value.value);
+    }
+
+    public void testWithDelegatingIntCreator() throws Exception
+    {
+        final double EXP = -3.75;
+        DoubleCreatorValue value = MAPPER.readValue(String.valueOf(EXP),
+                DoubleCreatorValue.class);        
+        assertEquals(EXP, value.value);
+    }
+
+    public void testWithDelegatingBooleanCreator() throws Exception
+    {
+        final boolean EXP = true;
+        BooleanCreatorValue value = MAPPER.readValue(String.valueOf(EXP),
+                BooleanCreatorValue.class);        
+        assertEquals(EXP, value.value);
+    }
+}
+
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderWithUnwrappedTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderWithUnwrappedTest.java
index 89726cb..a787584 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderWithUnwrappedTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderWithUnwrappedTest.java
@@ -8,13 +8,8 @@
 import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
 import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
 
-public class BuilderWithUnwrappedTest extends BaseMapTest {
-    /*
-     *************************************
-     * Mock classes
-     *************************************
-     */
-
+public class BuilderWithUnwrappedTest extends BaseMapTest
+{
     final static class Name {
         private final String first;
         private final String last;
@@ -161,9 +156,9 @@
     }
 
     /*
-     *************************************
-     * Unit tests
-     *************************************
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
      */
 
     public void testWithUnwrappedAndCreatorSingleParameterAtBeginning() throws Exception {
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderWithViewTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderWithViewTest.java
new file mode 100644
index 0000000..346112e
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/builder/BuilderWithViewTest.java
@@ -0,0 +1,114 @@
+package com.fasterxml.jackson.databind.deser.builder;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+
+public class BuilderWithViewTest extends BaseMapTest
+{
+    static class ViewX { }
+    static class ViewY { }
+
+    @JsonDeserialize(builder=SimpleBuilderXY.class)
+    static class ValueClassXY
+    {
+        final int _x, _y;
+
+        protected ValueClassXY(int x, int y) {
+            _x = x+1;
+            _y = y+1;
+        }
+    }
+
+    static class SimpleBuilderXY
+    {
+        public int x, y;
+
+        @JsonView(ViewX.class)
+        public SimpleBuilderXY withX(int x0) {
+              this.x = x0;
+              return this;
+        }
+
+        @JsonView(ViewY.class)
+        public SimpleBuilderXY withY(int y0) {
+              this.y = y0;
+              return this;
+        }
+
+        public ValueClassXY build() {
+              return new ValueClassXY(x, y);
+        }
+    }
+
+    @JsonDeserialize(builder=CreatorBuilderXY.class)
+    static class CreatorValueXY
+    {
+        final int _x, _y;
+
+        protected CreatorValueXY(int x, int y) {
+            _x = x;
+            _y = y;
+        }
+    }
+
+    @JsonIgnoreProperties({ "bogus" })
+    static class CreatorBuilderXY
+    {
+        public int x, y;
+
+        @JsonCreator
+        public CreatorBuilderXY(@JsonProperty("x") @JsonView(ViewX.class) int x,
+                @JsonProperty("y") @JsonView(ViewY.class) int y)
+        {
+            this.x = x;
+            this.y = y;
+        }
+
+        public CreatorValueXY build() {
+              return new CreatorValueXY(x, y);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+
+    public void testSimpleViews() throws Exception
+    {
+        final String json = aposToQuotes("{'x':5,'y':10}");
+        ValueClassXY resultX = MAPPER.readerFor(ValueClassXY.class)
+                .withView(ViewX.class)
+                .readValue(json);
+        assertEquals(6, resultX._x);
+        assertEquals(1, resultX._y);
+
+        ValueClassXY resultY = MAPPER.readerFor(ValueClassXY.class)
+                .withView(ViewY.class)
+                .readValue(json);
+        assertEquals(1, resultY._x);
+        assertEquals(11, resultY._y);
+    }
+
+    public void testCreatorViews() throws Exception
+    {
+        final String json = aposToQuotes("{'x':5,'y':10,'bogus':false}");
+        CreatorValueXY resultX = MAPPER.readerFor(CreatorValueXY.class)
+                .withView(ViewX.class)
+                .readValue(json);
+        assertEquals(5, resultX._x);
+        assertEquals(0, resultX._y);
+
+        CreatorValueXY resultY = MAPPER.readerFor(CreatorValueXY.class)
+                .withView(ViewY.class)
+                .readValue(json);
+        assertEquals(0, resultY._x);
+        assertEquals(10, resultY._y);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/ArrayDelegatorCreatorForCollectionTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/ArrayDelegatorCreatorForCollectionTest.java
similarity index 94%
rename from src/test/java/com/fasterxml/jackson/databind/creators/ArrayDelegatorCreatorForCollectionTest.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/creators/ArrayDelegatorCreatorForCollectionTest.java
index 4110008..62e4d63 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/ArrayDelegatorCreatorForCollectionTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/ArrayDelegatorCreatorForCollectionTest.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.creators;
+package com.fasterxml.jackson.databind.deser.creators;
 
 import java.util.Collections;
 import java.util.Set;
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/BigCreatorTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/BigCreatorTest.java
similarity index 97%
rename from src/test/java/com/fasterxml/jackson/databind/creators/BigCreatorTest.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/creators/BigCreatorTest.java
index 154d1a5..4d43c56 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/BigCreatorTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/BigCreatorTest.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.creators;
+package com.fasterxml.jackson.databind.deser.creators;
 
 import com.fasterxml.jackson.annotation.*;
 
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/CreatorPropertiesTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/CreatorPropertiesTest.java
similarity index 60%
rename from src/test/java/com/fasterxml/jackson/databind/creators/CreatorPropertiesTest.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/creators/CreatorPropertiesTest.java
index 93ad973..0e375dd 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/CreatorPropertiesTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/CreatorPropertiesTest.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.creators;
+package com.fasterxml.jackson.databind.deser.creators;
 
 import java.beans.ConstructorProperties;
 
@@ -16,7 +16,7 @@
 
         @ConstructorProperties({"x", "y"})
         // Same as above; use differing local parameter names so that parameter name
-        // introspection can not be used as the source of property names.
+        // introspection cannot be used as the source of property names.
         public Issue905Bean(int a, int b) {
             _x = a;
             _y = b;
@@ -25,7 +25,6 @@
 
     // for [databind#1122]
     static class Ambiguity {
-
         @JsonProperty("bar")
         private int foo;
 
@@ -46,6 +45,19 @@
         }
     }
 
+    // for [databind#1371]
+    static class Lombok1371Bean {
+        public int x, y;
+
+        protected Lombok1371Bean() { }
+
+        @ConstructorProperties({ "x", "y" })
+        public Lombok1371Bean(int _x, int _y) {
+            x = _x + 1;
+            y = _y + 1;
+        }
+    }
+
     /*
     /**********************************************************
     /* Test methods
@@ -71,4 +83,24 @@
         assertNotNull(amb);
         assertEquals(3, amb.getFoo());
     }
+
+    // [databind#1371]: MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES
+    public void testConstructorPropertiesInference() throws Exception
+    {
+        final String JSON = aposToQuotes("{'x':3,'y':5}");
+
+        // by default, should detect and use arguments-taking constructor as creator
+        assertTrue(MAPPER.isEnabled(MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES));
+        Lombok1371Bean result = MAPPER.readValue(JSON, Lombok1371Bean.class);
+        assertEquals(4, result.x);
+        assertEquals(6, result.y);
+
+        // but change if configuration changed
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.disable(MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES);
+        // in which case fields are set directly:
+        result = mapper.readValue(JSON, Lombok1371Bean.class);
+        assertEquals(3, result.x);
+        assertEquals(5, result.y);
+    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/creators/CreatorWithNamingStrategyTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/CreatorWithNamingStrategyTest.java
new file mode 100644
index 0000000..83f5970
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/CreatorWithNamingStrategyTest.java
@@ -0,0 +1,54 @@
+package com.fasterxml.jackson.databind.deser.creators;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
+import com.fasterxml.jackson.databind.introspect.AnnotatedParameter;
+import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
+
+public class CreatorWithNamingStrategyTest extends BaseMapTest
+{
+    @SuppressWarnings("serial")
+    static class MyParamIntrospector extends JacksonAnnotationIntrospector
+    {
+        @Override
+        public String findImplicitPropertyName(AnnotatedMember param) {
+            if (param instanceof AnnotatedParameter) {
+                AnnotatedParameter ap = (AnnotatedParameter) param;
+                return "paramName"+ap.getIndex();
+            }
+            return super.findImplicitPropertyName(param);
+        }
+    }
+
+    // [databind#2051]
+    static class OneProperty {
+        public String paramName0;
+
+        @JsonCreator
+        public OneProperty(String bogus) {
+            paramName0 = "CTOR:"+bogus;
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = newObjectMapper()
+            .setAnnotationIntrospector(new MyParamIntrospector())
+            .setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE)
+            ;
+
+    // [databind#2051]
+    public void testSnakeCaseWithOneArg() throws Exception
+    {
+        final String MSG = "1st";
+        OneProperty actual = MAPPER.readValue(
+                "{\"param_name0\":\""+MSG+"\"}",
+                OneProperty.class);
+        assertEquals("CTOR:"+MSG, actual.paramName0);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/CreatorWithObjectIdTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/CreatorWithObjectIdTest.java
similarity index 95%
rename from src/test/java/com/fasterxml/jackson/databind/creators/CreatorWithObjectIdTest.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/creators/CreatorWithObjectIdTest.java
index 01c13a0..a82c7e2 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/CreatorWithObjectIdTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/CreatorWithObjectIdTest.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.creators;
+package com.fasterxml.jackson.databind.deser.creators;
 
 import java.beans.ConstructorProperties;
 
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/DelegatingArrayCreator1804Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/DelegatingArrayCreator1804Test.java
similarity index 94%
rename from src/test/java/com/fasterxml/jackson/databind/creators/DelegatingArrayCreator1804Test.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/creators/DelegatingArrayCreator1804Test.java
index 328f4bc..04e0423 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/DelegatingArrayCreator1804Test.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/DelegatingArrayCreator1804Test.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.creators;
+package com.fasterxml.jackson.databind.deser.creators;
 
 import java.util.List;
 
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/creators/DelegatingCreatorAnnotations2016Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/DelegatingCreatorAnnotations2016Test.java
new file mode 100644
index 0000000..e892918
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/DelegatingCreatorAnnotations2016Test.java
@@ -0,0 +1,56 @@
+package com.fasterxml.jackson.databind.deser.creators;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+
+// Tests for problems uncovered with [databind#2016]; related to
+// `@JsonDeserialize` modifications to type, deserializer(s)
+public class DelegatingCreatorAnnotations2016Test extends BaseMapTest
+{
+    // [databind#2016]
+    static class Wrapper2016As {
+        Object value;
+
+        @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
+        public Wrapper2016As(@JsonDeserialize(as = java.util.Date.class) Object v) {
+            value = v;
+        }
+    }
+
+    static class Wrapper2016ContentAs {
+        List<Object> value;
+
+        @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
+        public Wrapper2016ContentAs(@JsonDeserialize(contentAs = java.util.Date.class) List<Object> v) {
+            value = v;
+        }
+    }
+
+    /*
+    /**********************************************************************
+    /* Test methods
+    /**********************************************************************
+     */
+
+    private final ObjectMapper MAPPER = newObjectMapper();
+
+    // [databind#2016]
+    public void testDelegatingWithAs() throws Exception
+    {
+        Wrapper2016As actual = MAPPER.readValue("123", Wrapper2016As.class);
+        assertEquals(Date.class, actual.value.getClass());
+    }
+
+    public void testDelegatingWithContentAs() throws Exception
+    {
+        Wrapper2016ContentAs actual = MAPPER.readValue("[123]", Wrapper2016ContentAs.class);
+        List<Object> l = actual.value;
+        assertEquals(1, l.size());
+        assertEquals(Date.class, l.get(0).getClass());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/creators/DelegatingCreatorAnnotations2021Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/DelegatingCreatorAnnotations2021Test.java
new file mode 100644
index 0000000..aa6eaa1
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/DelegatingCreatorAnnotations2021Test.java
@@ -0,0 +1,58 @@
+package com.fasterxml.jackson.databind.deser.creators;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+
+import com.fasterxml.jackson.core.JsonParser;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+
+// Tests for problems uncovered with [databind#2016]; related to
+// `@JsonDeserialize` modifications to type, deserializer(s)
+@SuppressWarnings("serial")
+public class DelegatingCreatorAnnotations2021Test extends BaseMapTest
+{
+    // [databind#2021]
+    static class DelegatingWithCustomDeser2021 {
+        public final static Double DEFAULT = 0.25;
+
+        Number value;
+
+        @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
+        public DelegatingWithCustomDeser2021(@JsonDeserialize(using = ValueDeser2021.class) Number v) {
+            value = v;
+        }
+    }
+
+    static class ValueDeser2021 extends StdDeserializer<Number> {
+        public ValueDeser2021() { super(Number.class); }
+
+        @Override
+        public Number deserialize(JsonParser p, DeserializationContext ctxt)
+            throws IOException
+        {
+            p.skipChildren();
+            return DelegatingWithCustomDeser2021.DEFAULT;
+        }
+    }
+
+    /*
+    /**********************************************************************
+    /* Test methods
+    /**********************************************************************
+     */
+
+    private final ObjectMapper MAPPER = newObjectMapper();
+
+    // [databind#2021]
+    public void testCustomDeserForDelegating() throws Exception
+    {
+        DelegatingWithCustomDeser2021 actual = MAPPER.readValue(" true ", DelegatingWithCustomDeser2021.class);
+        assertEquals(DelegatingWithCustomDeser2021.DEFAULT, actual.value);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/DelegatingCreatorImplicitNames1001Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/DelegatingCreatorImplicitNames1001Test.java
similarity index 97%
rename from src/test/java/com/fasterxml/jackson/databind/creators/DelegatingCreatorImplicitNames1001Test.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/creators/DelegatingCreatorImplicitNames1001Test.java
index ee354cb..c0043c9 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/DelegatingCreatorImplicitNames1001Test.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/DelegatingCreatorImplicitNames1001Test.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.creators;
+package com.fasterxml.jackson.databind.deser.creators;
 
 import com.fasterxml.jackson.annotation.*;
 import com.fasterxml.jackson.databind.*;
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/DelegatingExternalProperty1003Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/DelegatingExternalProperty1003Test.java
similarity index 96%
rename from src/test/java/com/fasterxml/jackson/databind/creators/DelegatingExternalProperty1003Test.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/creators/DelegatingExternalProperty1003Test.java
index 2060d81..03c4f4d 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/DelegatingExternalProperty1003Test.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/DelegatingExternalProperty1003Test.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.creators;
+package com.fasterxml.jackson.databind.deser.creators;
 
 import com.fasterxml.jackson.annotation.*;
 
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/DisablingCreatorsTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/DisablingCreatorsTest.java
similarity index 96%
rename from src/test/java/com/fasterxml/jackson/databind/creators/DisablingCreatorsTest.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/creators/DisablingCreatorsTest.java
index 1ca6cf4..03acdf0 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/DisablingCreatorsTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/DisablingCreatorsTest.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.creators;
+package com.fasterxml.jackson.databind.deser.creators;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/EnumCreatorTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/EnumCreatorTest.java
similarity index 99%
rename from src/test/java/com/fasterxml/jackson/databind/creators/EnumCreatorTest.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/creators/EnumCreatorTest.java
index 1cf89c2..5024cea 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/EnumCreatorTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/EnumCreatorTest.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.creators;
+package com.fasterxml.jackson.databind.deser.creators;
 
 import java.math.BigDecimal;
 import java.util.*;
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/FailOnNullCreatorTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/FailOnNullCreatorTest.java
similarity index 97%
rename from src/test/java/com/fasterxml/jackson/databind/creators/FailOnNullCreatorTest.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/creators/FailOnNullCreatorTest.java
index 52416f4..a767bda 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/FailOnNullCreatorTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/FailOnNullCreatorTest.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.creators;
+package com.fasterxml.jackson.databind.deser.creators;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/ImplicitNameMatch792Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/ImplicitNameMatch792Test.java
similarity index 98%
rename from src/test/java/com/fasterxml/jackson/databind/creators/ImplicitNameMatch792Test.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/creators/ImplicitNameMatch792Test.java
index 887cdf4..8eafb11 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/ImplicitNameMatch792Test.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/ImplicitNameMatch792Test.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.creators;
+package com.fasterxml.jackson.databind.deser.creators;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.annotation.JsonPropertyOrder;
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/ImplicitParamsForCreatorTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/ImplicitParamsForCreatorTest.java
similarity index 96%
rename from src/test/java/com/fasterxml/jackson/databind/creators/ImplicitParamsForCreatorTest.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/creators/ImplicitParamsForCreatorTest.java
index a52c55a..13da73a 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/ImplicitParamsForCreatorTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/ImplicitParamsForCreatorTest.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.creators;
+package com.fasterxml.jackson.databind.deser.creators;
 
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/InnerClassCreatorTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/InnerClassCreatorTest.java
similarity index 91%
rename from src/test/java/com/fasterxml/jackson/databind/creators/InnerClassCreatorTest.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/creators/InnerClassCreatorTest.java
index c235388..eeb733e 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/InnerClassCreatorTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/InnerClassCreatorTest.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.creators;
+package com.fasterxml.jackson.databind.deser.creators;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
@@ -6,7 +6,7 @@
 
 // For [databind#1501], [databind#1502], [databind#1503]; mostly to
 // test that for non-static inner classes constructors are ignored
-// and no Creators should be processed (since they can not be made
+// and no Creators should be processed (since they cannot be made
 // to work in standard way anyway).
 public class InnerClassCreatorTest extends BaseMapTest
 {
@@ -64,7 +64,7 @@
             MAPPER.readValue(ser, Something1501.class);
             fail("Should not pass");
         } catch (JsonMappingException e) {
-            verifyException(e, "Can not construct instance");
+            verifyException(e, "Cannot construct instance");
             verifyException(e, "InnerSomething1501");
             verifyException(e, "can only instantiate non-static inner class by using default");
         }
@@ -77,7 +77,7 @@
             MAPPER.readValue(ser, Something1502.class);
             fail("Should not pass");
         } catch (JsonMappingException e) {
-            verifyException(e, "Can not construct instance");
+            verifyException(e, "Cannot construct instance");
             verifyException(e, "InnerSomething1502");
             verifyException(e, "can only instantiate non-static inner class by using default");
         }
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/MultiArgConstructorTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/MultiArgConstructorTest.java
similarity index 88%
rename from src/test/java/com/fasterxml/jackson/databind/creators/MultiArgConstructorTest.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/creators/MultiArgConstructorTest.java
index 883457c..72e39b7 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/MultiArgConstructorTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/MultiArgConstructorTest.java
@@ -1,9 +1,11 @@
-package com.fasterxml.jackson.databind.creators;
+package com.fasterxml.jackson.databind.deser.creators;
 
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
 import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.annotation.PropertyAccessor;
+
 import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
 import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
 import com.fasterxml.jackson.databind.introspect.AnnotatedParameter;
 import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
@@ -92,13 +94,15 @@
     {
         final ObjectMapper mapper = new ObjectMapper();
         mapper.setAnnotationIntrospector(new MyParamIntrospector());
-        mapper.setVisibility(PropertyAccessor.CREATOR, Visibility.NONE);
+        mapper.setDefaultVisibility(
+                JsonAutoDetect.Value.noOverrides()
+                    .withCreatorVisibility(Visibility.NONE));
         try {
             /*MultiArgCtorBean bean =*/ mapper.readValue(aposToQuotes("{'b':13,  'a':-99}"),
                 MultiArgCtorBean.class);
             fail("Should not have passed");
-        } catch (JsonMappingException e) {
-            verifyException(e, "No suitable constructor");
+        } catch (InvalidDefinitionException e) {
+            verifyException(e, "no Creators");
         }
     }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/RequiredCreatorTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/RequiredCreatorTest.java
similarity index 97%
rename from src/test/java/com/fasterxml/jackson/databind/creators/RequiredCreatorTest.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/creators/RequiredCreatorTest.java
index 67a7e3a..66bd967 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/RequiredCreatorTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/RequiredCreatorTest.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.creators;
+package com.fasterxml.jackson.databind.deser.creators;
 
 import com.fasterxml.jackson.annotation.*;
 
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/SingleArgCreatorTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/SingleArgCreatorTest.java
similarity index 98%
rename from src/test/java/com/fasterxml/jackson/databind/creators/SingleArgCreatorTest.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/creators/SingleArgCreatorTest.java
index 04cb86f..90f9869 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/SingleArgCreatorTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/SingleArgCreatorTest.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.creators;
+package com.fasterxml.jackson.databind.deser.creators;
 
 import com.fasterxml.jackson.annotation.*;
 import com.fasterxml.jackson.databind.*;
@@ -208,4 +208,3 @@
         assertEquals(2, v.y);
     }
 }
-
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestConstructFromMap.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestConstructFromMap.java
similarity index 97%
rename from src/test/java/com/fasterxml/jackson/databind/creators/TestConstructFromMap.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/creators/TestConstructFromMap.java
index 84e8c7c..2f7f847 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/TestConstructFromMap.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestConstructFromMap.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.creators;
+package com.fasterxml.jackson.databind.deser.creators;
 
 import java.math.BigDecimal;
 import java.util.*;
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorNullValue.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreatorNullValue.java
similarity index 95%
rename from src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorNullValue.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreatorNullValue.java
index d41723e..9147b04 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorNullValue.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreatorNullValue.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.creators;
+package com.fasterxml.jackson.databind.deser.creators;
 
 import java.io.IOException;
 import java.util.UUID;
@@ -50,7 +50,7 @@
         }
     }
 
-    protected static class TestModule extends Module
+    protected static class TestModule extends com.fasterxml.jackson.databind.Module
     {
         @Override
         public String getModuleName() {
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorWithNamingStrategy556.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreatorWithNamingStrategy556.java
similarity index 97%
rename from src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorWithNamingStrategy556.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreatorWithNamingStrategy556.java
index 8c4993b..affafc8 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorWithNamingStrategy556.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreatorWithNamingStrategy556.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.creators;
+package com.fasterxml.jackson.databind.deser.creators;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.databind.*;
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorWithPolymorphic113.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreatorWithPolymorphic113.java
similarity index 96%
rename from src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorWithPolymorphic113.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreatorWithPolymorphic113.java
index eb39a57..75fb8af 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorWithPolymorphic113.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreatorWithPolymorphic113.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.creators;
+package com.fasterxml.jackson.databind.deser.creators;
 
 import com.fasterxml.jackson.annotation.*;
 
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreators.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreators.java
similarity index 98%
rename from src/test/java/com/fasterxml/jackson/databind/creators/TestCreators.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreators.java
index 543c0d7..07854bc 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreators.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreators.java
@@ -1,10 +1,11 @@
-package com.fasterxml.jackson.databind.creators;
+package com.fasterxml.jackson.databind.deser.creators;
 
 import java.util.*;
 
 import com.fasterxml.jackson.annotation.*;
 
 import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
 
 /**
  * Unit tests for verifying that it is possible to annotate
@@ -493,7 +494,7 @@
     {
         try {
             /*BrokenBean bean =*/ MAPPER.readValue("{ \"x\" : 42 }", BrokenBean.class);
-        } catch (JsonMappingException je) {
+        } catch (InvalidDefinitionException je) {
             verifyException(je, "has no property name");
         }
     }
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreators2.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreators2.java
similarity index 77%
rename from src/test/java/com/fasterxml/jackson/databind/creators/TestCreators2.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreators2.java
index f075bcb..f7b2f86 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreators2.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreators2.java
@@ -1,5 +1,5 @@
 
-package com.fasterxml.jackson.databind.creators;
+package com.fasterxml.jackson.databind.deser.creators;
 
 import java.io.IOException;
 import java.util.List;
@@ -79,16 +79,6 @@
         }
     }
 
-    static class MapBean
-    {
-        protected Map<String,Long> map;
-        
-        @JsonCreator
-        public MapBean(Map<String, Long> map) {
-            this.map = map;
-        }
-    }
-
     // For [JACKSON-470]: should be appropriately detected, reported error about
     static class BrokenCreatorBean
     {
@@ -159,6 +149,34 @@
         public String getItem() { return null; }
     }
 
+    static final class MultiPropCreator1476 {
+        private final int intField;
+        private final String stringField;
+
+        public MultiPropCreator1476(@JsonProperty("intField") int intField) {
+          this(intField, "empty");
+        }
+
+        public MultiPropCreator1476(@JsonProperty("stringField") String stringField) {
+          this(-1, stringField);
+        }
+
+        @JsonCreator
+        public MultiPropCreator1476(@JsonProperty("intField") int intField,
+                @JsonProperty("stringField") String stringField) {
+          this.intField = intField;
+          this.stringField = stringField;
+        }
+
+        public int getIntField() {
+          return intField;
+        }
+
+        public String getStringField() {
+          return stringField;
+        }
+    }
+
     /*
     /**********************************************************
     /* Test methods
@@ -176,6 +194,9 @@
             verifyException(e, ": foobar");
             // also: should have nested exception
             Throwable t = e.getCause();
+            if (t == null) {
+                fail("Should have assigned cause for: ("+e.getClass().getSimpleName()+") "+e);
+            }
             assertNotNull(t);
             assertEquals(IllegalArgumentException.class, t.getClass());
             assertEquals("foobar", t.getMessage());
@@ -210,47 +231,27 @@
         assertNotNull(foo);
     }
 
-    // Catch and rethrow exceptions that Creator methods throw
+    // Catch and re-throw exceptions that Creator methods throw
     public void testJackson438() throws Exception
     {
+        Exception e = null;
         try {
             MAPPER.readValue("{ \"name\":\"foobar\" }", BeanFor438.class);
             fail("Should have failed");
-        } catch (Exception e) {
-            if (!(e instanceof JsonMappingException)) {
-                fail("Should have received JsonMappingException, caught "+e.getClass().getName());
-            }
-            verifyException(e, "don't like that name");
-            // Ok: also, let's ensure root cause is directly linked, without other extra wrapping:
-            Throwable t = e.getCause();
-            assertNotNull(t);
-            assertEquals(IllegalArgumentException.class, t.getClass());
-            verifyException(e, "don't like that name");
+        } catch (Exception e0) {
+            e = e0;
         }
-    }
-
-    @SuppressWarnings("unchecked")
-    public void testIssue465() throws Exception
-    {
-        final String JSON = "{\"A\":12}";
-
-        // first, test with regular Map, non empty
-        Map<String,Long> map = MAPPER.readValue(JSON, Map.class);
-        assertEquals(1, map.size());
-        assertEquals(Integer.valueOf(12), map.get("A"));
-        
-        MapBean bean = MAPPER.readValue(JSON, MapBean.class);
-        assertEquals(1, bean.map.size());
-        assertEquals(Long.valueOf(12L), bean.map.get("A"));
-
-        // and then empty ones
-        final String EMPTY_JSON = "{}";
-
-        map = MAPPER.readValue(EMPTY_JSON, Map.class);
-        assertEquals(0, map.size());
-        
-        bean = MAPPER.readValue(EMPTY_JSON, MapBean.class);
-        assertEquals(0, bean.map.size());
+        if (!(e instanceof JsonMappingException)) {
+            fail("Should have received JsonMappingException, caught "+e.getClass().getName());
+        }
+        verifyException(e, "don't like that name");
+        // Ok: also, let's ensure root cause is directly linked, without other extra wrapping:
+        Throwable t = e.getCause();
+        if (t == null) {
+            fail("Should have assigned cause for: ("+e.getClass().getSimpleName()+") "+e);
+        }
+        assertEquals(IllegalArgumentException.class, t.getClass());
+        verifyException(e, "don't like that name");
     }
 
     public void testCreatorWithDupNames() throws Exception
@@ -260,6 +261,8 @@
             fail("Should have caught duplicate creator parameters");
         } catch (JsonMappingException e) {
             verifyException(e, "duplicate creator property \"bar\"");
+            verifyException(e, "for type `com.fasterxml.jackson.databind.");
+            verifyException(e, "$BrokenCreatorBean`");
         }
     }
 
@@ -294,4 +297,13 @@
         Issue700Bean value = MAPPER.readValue("{ \"item\" : \"foo\" }", Issue700Bean.class);
         assertNotNull(value);
     }
+
+    // [databind#1476]
+    public void testConstructorChoice() throws Exception {
+        ObjectMapper mapper = new ObjectMapper();
+        MultiPropCreator1476 pojo = mapper.readValue("{ \"intField\": 1, \"stringField\": \"foo\" }",
+                MultiPropCreator1476.class);
+        assertEquals(1, pojo.getIntField());
+        assertEquals("foo", pojo.getStringField());
+    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreators3.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreators3.java
new file mode 100644
index 0000000..d00030c
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreators3.java
@@ -0,0 +1,209 @@
+package com.fasterxml.jackson.databind.deser.creators;
+
+import java.util.List;
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
+import com.fasterxml.jackson.databind.introspect.AnnotatedParameter;
+import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
+
+// Misc Creator tests, part 3
+public class TestCreators3 extends BaseMapTest
+{
+    static final class Foo {
+
+        @JsonProperty("foo")
+        protected Map<Integer, Bar> foo;
+        @JsonProperty("anumber")
+        protected long anumber;
+
+        public Foo() {
+            anumber = 0;
+        }
+
+        public Map<Integer, Bar> getFoo() {
+            return foo;
+        }
+
+        public long getAnumber() {
+            return anumber;
+        }
+    }
+
+    static final class Bar {
+
+        private final long p;
+        private final List<String> stuff;
+
+        @JsonCreator
+        public Bar(@JsonProperty("p") long p, @JsonProperty("stuff") List<String> stuff) {
+            this.p = p;
+            this.stuff = stuff;
+        }
+
+        @JsonProperty("s")
+        public List<String> getStuff() {
+            return stuff;
+        }
+
+        @JsonProperty("stuff")
+        private List<String> getStuffDeprecated() {
+            return stuff;
+        }
+
+        public long getP() {
+            return p;
+        }
+    }
+
+    // [databind#421]
+
+    static class MultiCtor
+    {
+        protected String _a, _b;
+        
+        private MultiCtor() { }
+        private MultiCtor(String a, String b, Boolean c) {
+            if (c == null) {
+                throw new RuntimeException("Wrong factory!");
+            }
+            _a = a;
+            _b = b;
+        }
+
+        @JsonCreator
+        static MultiCtor factory(@JsonProperty("a") String a, @JsonProperty("b") String b) {
+            return new MultiCtor(a, b, Boolean.TRUE);
+        }
+    }
+
+    @SuppressWarnings("serial")
+    static class MyParamIntrospector extends JacksonAnnotationIntrospector
+    {
+        @Override
+        public String findImplicitPropertyName(AnnotatedMember param) {
+            if (param instanceof AnnotatedParameter) {
+                AnnotatedParameter ap = (AnnotatedParameter) param;
+                switch (ap.getIndex()) {
+                case 0: return "a";
+                case 1: return "b";
+                case 2: return "c";
+                default:
+                    return "param"+ap.getIndex();
+                }
+            }
+            return super.findImplicitPropertyName(param);
+        }
+    }
+ 
+    // [databind#1853]
+    public static class Product1853 {
+        String name;
+
+        public Object other, errors;
+
+        @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
+        public Product1853(@JsonProperty("name") String name) {
+            this.name = "PROP:" + name;
+        }
+
+        @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
+        public static Product1853 from(String name){
+            return new Product1853(false, "DELEG:"+name);
+        }
+
+        private Product1853(boolean bogus, String name) {
+            this.name = name;
+        }
+
+        @JsonValue
+        public String getName(){
+            return name;
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = newObjectMapper();
+    
+    public void testCreator541() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.disable(
+                MapperFeature.AUTO_DETECT_CREATORS,
+                MapperFeature.AUTO_DETECT_FIELDS,
+                MapperFeature.AUTO_DETECT_GETTERS,
+                MapperFeature.AUTO_DETECT_IS_GETTERS,
+                MapperFeature.AUTO_DETECT_SETTERS,
+                MapperFeature.USE_GETTERS_AS_SETTERS
+        );
+        mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
+        mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
+        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);  
+
+        final String JSON = "{\n"
+                + "    \"foo\": {\n"
+                + "        \"0\": {\n"
+                + "            \"p\": 0,\n"
+                + "            \"stuff\": [\n"
+                + "              \"a\", \"b\" \n"
+                + "            ]   \n"
+                + "        },\n"
+                + "        \"1\": {\n"
+                + "            \"p\": 1000,\n"
+                + "            \"stuff\": [\n"
+                + "              \"c\", \"d\" \n"
+                + "            ]   \n"
+                + "        },\n"
+                + "        \"2\": {\n"
+                + "            \"p\": 2000,\n"
+                + "            \"stuff\": [\n"
+                + "            ]   \n"
+                + "        }\n"
+                + "    },\n"
+                + "    \"anumber\": 25385874\n"
+                + "}";
+
+        Foo obj = mapper.readValue(JSON, Foo.class);
+        assertNotNull(obj);
+        assertNotNull(obj.foo);
+        assertEquals(3, obj.foo.size());
+        assertEquals(25385874L, obj.getAnumber());
+    }
+
+    // [databind#421]
+    public void testMultiCtor421() throws Exception
+    {
+        final ObjectMapper mapper = newObjectMapper();
+        mapper.setAnnotationIntrospector(new MyParamIntrospector());
+
+        MultiCtor bean = mapper.readValue(aposToQuotes("{'a':'123','b':'foo'}"), MultiCtor.class);
+        assertNotNull(bean);
+        assertEquals("123", bean._a);
+        assertEquals("foo", bean._b);
+    }
+
+    // [databind#1853]
+    public void testSerialization() throws Exception {
+        assertEquals(quote("testProduct"),
+                MAPPER.writeValueAsString(new Product1853(false, "testProduct")));
+    }
+
+    public void testDeserializationFromObject() throws Exception {
+        final String EXAMPLE_DATA = "{\"name\":\"dummy\",\"other\":{},\"errors\":{}}";
+        assertEquals("PROP:dummy", MAPPER.readValue(EXAMPLE_DATA, Product1853.class).getName());
+    }
+
+    public void testDeserializationFromString() throws Exception {
+        assertEquals("DELEG:testProduct",
+                MAPPER.readValue(quote("testProduct"), Product1853.class).getName());
+    }
+}
+
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorsDelegating.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreatorsDelegating.java
similarity index 76%
rename from src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorsDelegating.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreatorsDelegating.java
index 0989f8b..85b028f 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorsDelegating.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreatorsDelegating.java
@@ -1,7 +1,9 @@
-package com.fasterxml.jackson.databind.creators;
+package com.fasterxml.jackson.databind.deser.creators;
 
-import com.fasterxml.jackson.annotation.JsonCreator;
+import java.util.*;
+
 import com.fasterxml.jackson.annotation.JacksonInject;
+import com.fasterxml.jackson.annotation.JsonCreator;
 
 import com.fasterxml.jackson.core.JsonParser;
 import com.fasterxml.jackson.core.JsonToken;
@@ -70,21 +72,32 @@
         }
     }
 
+    static class MapBean
+    {
+        protected Map<String,Long> map;
+        
+        @JsonCreator
+        public MapBean(Map<String, Long> map) {
+            this.map = map;
+        }
+    }
+
     /*
     /**********************************************************
-    /* Unit tests
+    /* Test methods
     /**********************************************************
      */
 
+    private final ObjectMapper MAPPER = newObjectMapper();
+    
     public void testBooleanDelegate() throws Exception
     {
-        ObjectMapper m = new ObjectMapper();
         // should obviously work with booleans...
-        BooleanBean bb = m.readValue("true", BooleanBean.class);
+        BooleanBean bb = MAPPER.readValue("true", BooleanBean.class);
         assertEquals(Boolean.TRUE, bb.value);
 
         // but also with value conversion from String
-        bb = m.readValue(quote("true"), BooleanBean.class);
+        bb = MAPPER.readValue(quote("true"), BooleanBean.class);
         assertEquals(Boolean.TRUE, bb.value);
     }
     
@@ -125,8 +138,7 @@
     // [databind#592]
     public void testDelegateWithTokenBuffer() throws Exception
     {
-        ObjectMapper mapper = new ObjectMapper();
-        Value592 value = mapper.readValue("{\"a\":1,\"b\":2}", Value592.class);
+        Value592 value = MAPPER.readValue("{\"a\":1,\"b\":2}", Value592.class);
         assertNotNull(value);
         Object ob = value.stuff;
         assertEquals(TokenBuffer.class, ob.getClass());
@@ -144,4 +156,27 @@
         jp.close();
     }
 
+    @SuppressWarnings("unchecked")
+    public void testIssue465() throws Exception
+    {
+        final String JSON = "{\"A\":12}";
+
+        // first, test with regular Map, non empty
+        Map<String,Long> map = MAPPER.readValue(JSON, Map.class);
+        assertEquals(1, map.size());
+        assertEquals(Integer.valueOf(12), map.get("A"));
+        
+        MapBean bean = MAPPER.readValue(JSON, MapBean.class);
+        assertEquals(1, bean.map.size());
+        assertEquals(Long.valueOf(12L), bean.map.get("A"));
+
+        // and then empty ones
+        final String EMPTY_JSON = "{}";
+
+        map = MAPPER.readValue(EMPTY_JSON, Map.class);
+        assertEquals(0, map.size());
+        
+        bean = MAPPER.readValue(EMPTY_JSON, MapBean.class);
+        assertEquals(0, bean.map.size());
+    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorsWithIdentity.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreatorsWithIdentity.java
similarity index 96%
rename from src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorsWithIdentity.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreatorsWithIdentity.java
index b064424..386802a 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorsWithIdentity.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCreatorsWithIdentity.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.creators;
+package com.fasterxml.jackson.databind.deser.creators;
 
 import java.io.IOException;
 
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestCustomValueInstDefaults.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCustomValueInstDefaults.java
similarity index 99%
rename from src/test/java/com/fasterxml/jackson/databind/creators/TestCustomValueInstDefaults.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCustomValueInstDefaults.java
index e9793e3..79edd02 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/TestCustomValueInstDefaults.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestCustomValueInstDefaults.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.creators;
+package com.fasterxml.jackson.databind.deser.creators;
 
 import java.io.IOException;
 
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestPolymorphicCreators.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestPolymorphicCreators.java
similarity index 98%
rename from src/test/java/com/fasterxml/jackson/databind/creators/TestPolymorphicCreators.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/creators/TestPolymorphicCreators.java
index b407ba5..16c953f 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/TestPolymorphicCreators.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestPolymorphicCreators.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.creators;
+package com.fasterxml.jackson.databind.deser.creators;
 
 import com.fasterxml.jackson.annotation.*;
 
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestPolymorphicDelegating.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestPolymorphicDelegating.java
similarity index 95%
rename from src/test/java/com/fasterxml/jackson/databind/creators/TestPolymorphicDelegating.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/creators/TestPolymorphicDelegating.java
index 0eba60b..eff86e3 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/TestPolymorphicDelegating.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestPolymorphicDelegating.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.creators;
+package com.fasterxml.jackson.databind.deser.creators;
 
 import com.fasterxml.jackson.annotation.*;
 import com.fasterxml.jackson.databind.*;
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestValueInstantiator.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestValueInstantiator.java
similarity index 96%
rename from src/test/java/com/fasterxml/jackson/databind/creators/TestValueInstantiator.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/creators/TestValueInstantiator.java
index 744d3d0..a53ab8f 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/TestValueInstantiator.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/TestValueInstantiator.java
@@ -1,12 +1,15 @@
-package com.fasterxml.jackson.databind.creators;
+package com.fasterxml.jackson.databind.deser.creators;
 
 import java.io.IOException;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
 
 import com.fasterxml.jackson.core.Version;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.annotation.JsonValueInstantiator;
 import com.fasterxml.jackson.databind.deser.*;
+import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
 import com.fasterxml.jackson.databind.introspect.AnnotatedWithParams;
 import com.fasterxml.jackson.databind.module.SimpleModule;
 import com.fasterxml.jackson.databind.type.TypeFactory;
@@ -19,7 +22,7 @@
     static class MyBean
     {
         String _secret;
-        
+
         public MyBean(String s, boolean bogus) {
             _secret = s;
         }
@@ -28,16 +31,16 @@
     static class MysteryBean
     {
         Object value;
-        
+
         public MysteryBean(Object v) { value = v; }
     }
-    
+
     static class CreatorBean
     {
         String _secret;
 
         public String value;
-        
+
         protected CreatorBean(String s) {
             _secret = s;
         }
@@ -586,8 +589,10 @@
             MAPPER.readValue("{ }", MyBean.class);
             fail("Should not succeed");
         } catch (JsonMappingException e) {
-            verifyException(e, "Can not construct instance of");
-            verifyException(e, "missing default constructor");
+            verifyException(e, "Cannot construct instance of");
+            verifyException(e, "no Creators");
+            // as per [databind#1414], is definition problem
+            assertEquals(InvalidDefinitionException.class, e.getClass());
         }
     }
 
@@ -599,8 +604,10 @@
             MAPPER.readValue("\"foo\"", MyBean.class);
             fail("Should not succeed");
         } catch (JsonMappingException e) {
-            verifyException(e, "Can not construct instance of");
+            verifyException(e, "Cannot construct instance of");
             verifyException(e, "no String-argument constructor/factory");
+            // as per [databind#1414], is definition problem
+            assertEquals(InvalidDefinitionException.class, e.getClass());
         }
     }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/filter/IgnoreCreatorProp1317Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/filter/IgnoreCreatorProp1317Test.java
similarity index 89%
rename from src/test/java/com/fasterxml/jackson/databind/filter/IgnoreCreatorProp1317Test.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/filter/IgnoreCreatorProp1317Test.java
index 4df9611..7117007 100644
--- a/src/test/java/com/fasterxml/jackson/databind/filter/IgnoreCreatorProp1317Test.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/filter/IgnoreCreatorProp1317Test.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.filter;
+package com.fasterxml.jackson.databind.deser.filter;
 
 import java.beans.ConstructorProperties;
 
@@ -41,10 +41,10 @@
     }
 
     public void testThatJsonIgnoreWorksWithConstructorProperties() throws Exception {
+        ObjectMapper om = objectMapper();
         Testing testing = new Testing("shouldBeIgnored", "notIgnore");
-        ObjectMapper om = new ObjectMapper();
         String json = om.writeValueAsString(testing);
-        System.out.println(json);
+//        System.out.println(json);
         assertFalse(json.contains("shouldBeIgnored"));
     }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/filter/IgnorePropertyOnDeserTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/filter/IgnorePropertyOnDeserTest.java
similarity index 95%
rename from src/test/java/com/fasterxml/jackson/databind/filter/IgnorePropertyOnDeserTest.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/filter/IgnorePropertyOnDeserTest.java
index 08b882d..5255d4b 100644
--- a/src/test/java/com/fasterxml/jackson/databind/filter/IgnorePropertyOnDeserTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/filter/IgnorePropertyOnDeserTest.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.filter;
+package com.fasterxml.jackson.databind.deser.filter;
 
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import com.fasterxml.jackson.annotation.JsonPropertyOrder;
@@ -40,8 +40,8 @@
     /* Unit tests
     /****************************************************************
      */
-    
-    private final ObjectMapper MAPPER = new ObjectMapper();
+
+    private final ObjectMapper MAPPER = newObjectMapper();
 
     // [databind#1217]
     public void testIgnoreOnProperty1217() throws Exception
@@ -54,7 +54,7 @@
 
         assertEquals(1, result.obj.x);
         assertEquals(2, result.obj2.y);
-        
+
         TestIgnoreObject result1 = MAPPER.readValue(
                   aposToQuotes("{'obj':{'x': 20, 'y': 30}, 'obj2':{'x': 20, 'y': 40}}"),
                   TestIgnoreObject.class);
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/filter/NullConversionsForContentTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/filter/NullConversionsForContentTest.java
new file mode 100644
index 0000000..dfb3d37
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/filter/NullConversionsForContentTest.java
@@ -0,0 +1,434 @@
+package com.fasterxml.jackson.databind.deser.filter;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.JsonSetter;
+import com.fasterxml.jackson.annotation.Nulls;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.exc.InvalidNullException;
+
+// For [databind#1402]; configurable null handling, for contents of
+// Collections, Maps, arrays
+public class NullConversionsForContentTest extends BaseMapTest
+{
+    static class NullContentFail<T> {
+        public T nullsOk;
+
+        @JsonSetter(contentNulls=Nulls.FAIL)
+        public T noNulls;
+    }
+
+    static class NullContentAsEmpty<T> {
+        @JsonSetter(contentNulls=Nulls.AS_EMPTY)
+        public T values;
+    }
+
+    static class NullContentSkip<T> {
+        @JsonSetter(contentNulls=Nulls.SKIP)
+        public T values;
+    }
+
+    static class NullContentUndefined<T> {
+        @JsonSetter // leave with defaults
+        public T values;
+    }
+    
+    /*
+    /**********************************************************
+    /* Test methods, fail-on-null
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = newObjectMapper();
+
+    // Tests to verify that we can set default settings for failure
+    public void testFailOnNullFromDefaults() throws Exception
+    {
+        final String JSON = aposToQuotes("{'values':[null]}");
+        TypeReference<?> listType = new TypeReference<NullContentUndefined<List<String>>>() { };
+
+        // by default fine to get nulls
+        NullContentUndefined<List<String>> result = MAPPER.readValue(JSON, listType);
+        assertNotNull(result.values);
+        assertEquals(1, result.values.size());
+        assertNull(result.values.get(0));
+
+        // but not when overridden globally:
+        ObjectMapper mapper = newObjectMapper();
+        mapper.setDefaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.FAIL));
+        try {
+            mapper.readValue(JSON, listType);
+            fail("Should not pass");
+        } catch (InvalidNullException e) {
+            verifyException(e, "property \"values\"");
+        }
+
+        // or configured for type:
+        mapper = newObjectMapper();
+        mapper.configOverride(List.class)
+                .setSetterInfo(JsonSetter.Value.forContentNulls(Nulls.FAIL));
+        try {
+            mapper.readValue(JSON, listType);
+            fail("Should not pass");
+        } catch (InvalidNullException e) {
+            verifyException(e, "property \"values\"");
+        }
+    }
+    
+    public void testFailOnNullWithCollections() throws Exception
+    {
+        TypeReference<?> typeRef = new TypeReference<NullContentFail<List<Integer>>>() { };
+
+        // first, ok if assigning non-null to not-nullable, null for nullable
+        NullContentFail<List<Integer>> result = MAPPER.readValue(aposToQuotes("{'nullsOk':[null]}"),
+                typeRef);
+        assertNotNull(result.nullsOk);
+        assertEquals(1, result.nullsOk.size());
+        assertNull(result.nullsOk.get(0));
+
+        // and then see that nulls are not ok for non-nullable.
+        
+        // List<Integer>
+        final String JSON = aposToQuotes("{'noNulls':[null]}");
+        try {
+            MAPPER.readValue(JSON, typeRef);
+            fail("Should not pass");
+        } catch (InvalidNullException e) {
+            verifyException(e, "property \"noNulls\"");
+        }
+
+        // List<String>
+        try {
+            MAPPER.readValue(JSON, new TypeReference<NullContentFail<List<String>>>() { });
+            fail("Should not pass");
+        } catch (InvalidNullException e) {
+            verifyException(e, "property \"noNulls\"");
+        }
+    }
+
+    public void testFailOnNullWithArrays() throws Exception
+    {
+        final String JSON = aposToQuotes("{'noNulls':[null]}");
+        // Object[]
+        try {
+            MAPPER.readValue(JSON, new TypeReference<NullContentFail<Object[]>>() { });
+            fail("Should not pass");
+        } catch (InvalidNullException e) {
+            verifyException(e, "property \"noNulls\"");
+        }
+
+        // String[]
+        try {
+            MAPPER.readValue(JSON, new TypeReference<NullContentFail<String[]>>() { });
+            fail("Should not pass");
+        } catch (InvalidNullException e) {
+            verifyException(e, "property \"noNulls\"");
+        }
+    }
+
+    public void testFailOnNullWithPrimitiveArrays() throws Exception
+    {
+        final String JSON = aposToQuotes("{'noNulls':[null]}");
+
+        // boolean[]
+        try {
+            MAPPER.readValue(JSON, new TypeReference<NullContentFail<boolean[]>>() { });
+            fail("Should not pass");
+        } catch (InvalidNullException e) {
+            verifyException(e, "property \"noNulls\"");
+        }
+        // int[]
+        try {
+            MAPPER.readValue(JSON, new TypeReference<NullContentFail<int[]>>() { });
+            fail("Should not pass");
+        } catch (InvalidNullException e) {
+            verifyException(e, "property \"noNulls\"");
+        }
+        // double[]
+        try {
+            MAPPER.readValue(JSON, new TypeReference<NullContentFail<double[]>>() { });
+            fail("Should not pass");
+        } catch (InvalidNullException e) {
+            verifyException(e, "property \"noNulls\"");
+        }
+    }
+
+    public void testFailOnNullWithMaps() throws Exception
+    {
+        // Then: Map<String,String>
+        try {
+            final String MAP_JSON = aposToQuotes("{'noNulls':{'a':null}}");
+            MAPPER.readValue(MAP_JSON, new TypeReference<NullContentFail<Map<String,String>>>() { });
+            fail("Should not pass");
+        } catch (InvalidNullException e) {
+            verifyException(e, "property \"noNulls\"");
+        }
+
+        // Then: EnumMap<Enum,String>
+        try {
+            final String MAP_JSON = aposToQuotes("{'noNulls':{'A':null}}");
+            MAPPER.readValue(MAP_JSON, new TypeReference<NullContentFail<EnumMap<ABC,String>>>() { });
+            fail("Should not pass");
+        } catch (InvalidNullException e) {
+            verifyException(e, "property \"noNulls\"");
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods, null-as-empty
+    /**********************************************************
+     */
+
+    public void testNullsAsEmptyWithCollections() throws Exception
+    {
+        final String JSON = aposToQuotes("{'values':[null]}");
+
+        // List<Integer>
+        {
+            NullContentAsEmpty<List<Integer>> result = MAPPER.readValue(JSON,
+                    new TypeReference<NullContentAsEmpty<List<Integer>>>() { });
+            assertEquals(1, result.values.size());
+            assertEquals(Integer.valueOf(0), result.values.get(0));
+        }
+
+        // List<String>
+        {
+            NullContentAsEmpty<List<String>> result = MAPPER.readValue(JSON,
+                    new TypeReference<NullContentAsEmpty<List<String>>>() { });
+            assertEquals(1, result.values.size());
+            assertEquals("", result.values.get(0));
+        }
+    }
+
+    public void testNullsAsEmptyUsingDefaults() throws Exception
+    {
+        final String JSON = aposToQuotes("{'values':[null]}");
+        TypeReference<?> listType = new TypeReference<NullContentUndefined<List<Integer>>>() { };
+
+        // Let's see defaulting in action
+        ObjectMapper mapper = newObjectMapper();
+        mapper.setDefaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.AS_EMPTY));
+        NullContentUndefined<List<Integer>> result = mapper.readValue(JSON, listType);
+        assertEquals(1, result.values.size());
+        assertEquals(Integer.valueOf(0), result.values.get(0));
+
+        // or configured for type:
+        mapper = newObjectMapper();
+        mapper.configOverride(List.class)
+                .setSetterInfo(JsonSetter.Value.forContentNulls(Nulls.AS_EMPTY));
+        result = mapper.readValue(JSON, listType);
+        assertEquals(1, result.values.size());
+        assertEquals(Integer.valueOf(0), result.values.get(0));
+    }        
+    
+    public void testNullsAsEmptyWithArrays() throws Exception
+    {
+        // Note: skip `Object[]`, no default empty value at this point
+        final String JSON = aposToQuotes("{'values':[null]}");
+
+        // Then: String[]
+        {
+            NullContentAsEmpty<String[]> result = MAPPER.readValue(JSON,
+                    new TypeReference<NullContentAsEmpty<String[]>>() { });
+            assertEquals(1, result.values.length);
+            assertEquals("", result.values[0]);
+        }
+    }
+
+    public void testNullsAsEmptyWithPrimitiveArrays() throws Exception
+    {
+        final String JSON = aposToQuotes("{'values':[null]}");
+
+        // int[]
+        {
+            NullContentAsEmpty<int[]> result = MAPPER.readValue(JSON,
+                    new TypeReference<NullContentAsEmpty<int[]>>() { });
+            assertEquals(1, result.values.length);
+            assertEquals(0, result.values[0]);
+        }
+
+        // long[]
+        {
+            NullContentAsEmpty<long[]> result = MAPPER.readValue(JSON,
+                    new TypeReference<NullContentAsEmpty<long[]>>() { });
+            assertEquals(1, result.values.length);
+            assertEquals(0L, result.values[0]);
+        }
+
+        // boolean[]
+        {
+            NullContentAsEmpty<boolean[]> result = MAPPER.readValue(JSON,
+                    new TypeReference<NullContentAsEmpty<boolean[]>>() { });
+            assertEquals(1, result.values.length);
+            assertEquals(false, result.values[0]);
+        }
+}
+    
+    public void testNullsAsEmptyWithMaps() throws Exception
+    {
+        // Then: Map<String,String>
+        final String MAP_JSON = aposToQuotes("{'values':{'A':null}}");
+        {
+            NullContentAsEmpty<Map<String,String>> result 
+                = MAPPER.readValue(MAP_JSON, new TypeReference<NullContentAsEmpty<Map<String,String>>>() { });
+            assertEquals(1, result.values.size());
+            assertEquals("A", result.values.entrySet().iterator().next().getKey());
+            assertEquals("", result.values.entrySet().iterator().next().getValue());
+        }
+
+        // Then: EnumMap<Enum,String>
+        {
+            NullContentAsEmpty<EnumMap<ABC,String>> result 
+                = MAPPER.readValue(MAP_JSON, new TypeReference<NullContentAsEmpty<EnumMap<ABC,String>>>() { });
+            assertEquals(1, result.values.size());
+            assertEquals(ABC.A, result.values.entrySet().iterator().next().getKey());
+            assertEquals("", result.values.entrySet().iterator().next().getValue());
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods, skip-nulls
+    /**********************************************************
+     */
+
+    public void testNullsSkipUsingDefaults() throws Exception
+    {
+        final String JSON = aposToQuotes("{'values':[null]}");
+        TypeReference<?> listType = new TypeReference<NullContentUndefined<List<Long>>>() { };
+
+        // Let's see defaulting in action
+        ObjectMapper mapper = newObjectMapper();
+        mapper.setDefaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.SKIP));
+        NullContentUndefined<List<Long>> result = mapper.readValue(JSON, listType);
+        assertEquals(0, result.values.size());
+
+        // or configured for type:
+        mapper = newObjectMapper();
+        mapper.configOverride(List.class)
+                .setSetterInfo(JsonSetter.Value.forContentNulls(Nulls.SKIP));
+        result = mapper.readValue(JSON, listType);
+        assertEquals(0, result.values.size());
+    }        
+
+    // Test to verify that per-property setting overrides defaults:
+    public void testNullsSkipWithOverrides() throws Exception
+    {
+        final String JSON = aposToQuotes("{'values':[null]}");
+        TypeReference<?> listType = new TypeReference<NullContentSkip<List<Long>>>() { };
+
+        ObjectMapper mapper = newObjectMapper();
+        // defaults call for fail; but POJO specifies "skip"; latter should win
+        mapper.setDefaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.FAIL));
+        NullContentSkip<List<Long>> result = mapper.readValue(JSON, listType);
+        assertEquals(0, result.values.size());
+
+        // ditto for per-type defaults
+        mapper = newObjectMapper();
+        mapper.configOverride(List.class)
+                .setSetterInfo(JsonSetter.Value.forContentNulls(Nulls.FAIL));
+        result = mapper.readValue(JSON, listType);
+        assertEquals(0, result.values.size());
+    }        
+
+    public void testNullsSkipWithCollections() throws Exception
+    {
+        // List<Integer>
+        {
+            final String JSON = aposToQuotes("{'values':[1,null,2]}");
+            NullContentSkip<List<Integer>> result = MAPPER.readValue(JSON,
+                    new TypeReference<NullContentSkip<List<Integer>>>() { });
+            assertEquals(2, result.values.size());
+            assertEquals(Integer.valueOf(1), result.values.get(0));
+            assertEquals(Integer.valueOf(2), result.values.get(1));
+        }
+
+        // List<String>
+        {
+            final String JSON = aposToQuotes("{'values':['ab',null,'xy']}");
+            NullContentSkip<List<String>> result = MAPPER.readValue(JSON,
+                    new TypeReference<NullContentSkip<List<String>>>() { });
+            assertEquals(2, result.values.size());
+            assertEquals("ab", result.values.get(0));
+            assertEquals("xy", result.values.get(1));
+        }
+    }
+
+    public void testNullsSkipWithArrays() throws Exception
+    {
+        final String JSON = aposToQuotes("{'values':['a',null,'xy']}");
+        // Object[]
+        {
+            NullContentSkip<Object[]> result = MAPPER.readValue(JSON,
+                    new TypeReference<NullContentSkip<Object[]>>() { });
+            assertEquals(2, result.values.length);
+            assertEquals("a", result.values[0]);
+            assertEquals("xy", result.values[1]);
+        }
+        // String[]
+        {
+            NullContentSkip<String[]> result = MAPPER.readValue(JSON,
+                    new TypeReference<NullContentSkip<String[]>>() { });
+            assertEquals(2, result.values.length);
+            assertEquals("a", result.values[0]);
+            assertEquals("xy", result.values[1]);
+        }
+    }
+
+    public void testNullsSkipWithPrimitiveArrays() throws Exception
+    {
+        // int[]
+        {
+            final String JSON = aposToQuotes("{'values':[3,null,7]}");
+            NullContentSkip<int[]> result = MAPPER.readValue(JSON,
+                    new TypeReference<NullContentSkip<int[]>>() { });
+            assertEquals(2, result.values.length);
+            assertEquals(3, result.values[0]);
+            assertEquals(7, result.values[1]);
+        }
+
+        // long[]
+        {
+            final String JSON = aposToQuotes("{'values':[-13,null,999]}");
+            NullContentSkip<long[]> result = MAPPER.readValue(JSON,
+                    new TypeReference<NullContentSkip<long[]>>() { });
+            assertEquals(2, result.values.length);
+            assertEquals(-13L, result.values[0]);
+            assertEquals(999L, result.values[1]);
+        }
+
+        // boolean[]
+        {
+            final String JSON = aposToQuotes("{'values':[true,null,true]}");
+            NullContentSkip<boolean[]> result = MAPPER.readValue(JSON,
+                    new TypeReference<NullContentSkip<boolean[]>>() { });
+            assertEquals(2, result.values.length);
+            assertEquals(true, result.values[0]);
+            assertEquals(true, result.values[1]);
+        }
+    }
+    
+    public void testNullsSkipWithMaps() throws Exception
+    {
+        // Then: Map<String,String>
+        final String MAP_JSON = aposToQuotes("{'values':{'A':'foo','B':null,'C':'bar'}}");
+        {
+            NullContentSkip<Map<String,String>> result 
+                = MAPPER.readValue(MAP_JSON, new TypeReference<NullContentSkip<Map<String,String>>>() { });
+            assertEquals(2, result.values.size());
+            assertEquals("foo", result.values.get("A"));
+            assertEquals("bar", result.values.get("C"));
+        }
+
+        // Then: EnumMap<Enum,String>
+        {
+            NullContentSkip<EnumMap<ABC,String>> result 
+                = MAPPER.readValue(MAP_JSON, new TypeReference<NullContentSkip<EnumMap<ABC,String>>>() { });
+            assertEquals(2, result.values.size());
+            assertEquals("foo", result.values.get(ABC.A));
+            assertEquals("bar", result.values.get(ABC.C));
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/filter/NullConversionsGenericTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/filter/NullConversionsGenericTest.java
new file mode 100644
index 0000000..87df5f9
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/filter/NullConversionsGenericTest.java
@@ -0,0 +1,126 @@
+package com.fasterxml.jackson.databind.deser.filter;
+
+import java.util.List;
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonSetter;
+import com.fasterxml.jackson.annotation.Nulls;
+import com.fasterxml.jackson.core.type.TypeReference;
+
+import com.fasterxml.jackson.databind.*;
+
+// for [databind#1402]; configurable null handling, for values themselves,
+// using generic types
+public class NullConversionsGenericTest extends BaseMapTest
+{
+    static class GeneralEmpty<T> {
+        // 09-Feb-2017, tatu: Should only need annotation either for field OR setter, not both:
+//        @JsonSetter(nulls=JsonSetter.Nulls.AS_EMPTY)
+        T value;
+
+        @JsonSetter(nulls=Nulls.AS_EMPTY)
+        public void setValue(T v) {
+            value = v;
+        }
+    }
+
+    static class NoCtorWrapper {
+        @JsonSetter(nulls=Nulls.AS_EMPTY)
+        public NoCtorPOJO value;
+    }
+
+    static class NoCtorPOJO {
+        public NoCtorPOJO(boolean b) { }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = newObjectMapper();
+
+    public void testNullsToEmptyPojo() throws Exception
+    {
+        GeneralEmpty<Point> result = MAPPER.readValue(aposToQuotes("{'value':null}"),
+                new TypeReference<GeneralEmpty<Point>>() { });
+        assertNotNull(result.value);
+        Point p = result.value;
+        assertEquals(0, p.x);
+        assertEquals(0, p.y);
+
+        // and then also failing case with no suitable creator:
+        try {
+            /* NoCtorWrapper nogo =*/ MAPPER.readValue(aposToQuotes("{'value':null}"),
+                    NoCtorWrapper.class);
+            fail("Should not pass");
+        } catch (JsonMappingException e) {
+            verifyException(e, "Cannot create empty instance");
+        }
+    }
+
+    // [databind#2023] two-part coercion from "" to `null` to skip/empty/exception should work
+    public void testEmptyStringToNullToEmptyPojo() throws Exception
+    {
+        GeneralEmpty<Point> result = MAPPER.readerFor(new TypeReference<GeneralEmpty<Point>>() { })
+                .with(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)
+                .readValue(aposToQuotes("{'value':''}"));
+        assertNotNull(result.value);
+        Point p = result.value;
+        assertEquals(0, p.x);
+        assertEquals(0, p.y);
+    }
+
+    public void testNullsToEmptyCollection() throws Exception
+    {
+        GeneralEmpty<List<String>> result = MAPPER.readValue(aposToQuotes("{'value':null}"),
+                new TypeReference<GeneralEmpty<List<String>>>() { });
+        assertNotNull(result.value);
+        assertEquals(0, result.value.size());
+
+        // but also non-String type, since impls vary
+        GeneralEmpty<List<Integer>> result2 = MAPPER.readValue(aposToQuotes("{'value':null}"),
+                new TypeReference<GeneralEmpty<List<Integer>>>() { });
+        assertNotNull(result2.value);
+        assertEquals(0, result2.value.size());
+    }
+
+    public void testNullsToEmptyMap() throws Exception
+    {
+        GeneralEmpty<Map<String,String>> result = MAPPER.readValue(aposToQuotes("{'value':null}"),
+                new TypeReference<GeneralEmpty<Map<String,String>>>() { });
+        assertNotNull(result.value);
+        assertEquals(0, result.value.size());
+    }
+
+    public void testNullsToEmptyArrays() throws Exception
+    {
+        final String json = aposToQuotes("{'value':null}");
+
+        GeneralEmpty<Object[]> result = MAPPER.readValue(json,
+                new TypeReference<GeneralEmpty<Object[]>>() { });
+        assertNotNull(result.value);
+        assertEquals(0, result.value.length);
+
+        GeneralEmpty<String[]> result2 = MAPPER.readValue(json,
+                new TypeReference<GeneralEmpty<String[]>>() { });
+        assertNotNull(result2.value);
+        assertEquals(0, result2.value.length);
+
+        GeneralEmpty<int[]> result3 = MAPPER.readValue(json,
+                new TypeReference<GeneralEmpty<int[]>>() { });
+        assertNotNull(result3.value);
+        assertEquals(0, result3.value.length);
+
+        GeneralEmpty<double[]> result4 = MAPPER.readValue(json,
+                new TypeReference<GeneralEmpty<double[]>>() { });
+        assertNotNull(result4.value);
+        assertEquals(0, result4.value.length);
+
+        GeneralEmpty<boolean[]> result5 = MAPPER.readValue(json,
+                new TypeReference<GeneralEmpty<boolean[]>>() { });
+        assertNotNull(result5.value);
+        assertEquals(0, result5.value.length);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/filter/NullConversionsPojoTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/filter/NullConversionsPojoTest.java
new file mode 100644
index 0000000..b022fc6
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/filter/NullConversionsPojoTest.java
@@ -0,0 +1,107 @@
+package com.fasterxml.jackson.databind.deser.filter;
+
+import com.fasterxml.jackson.annotation.JsonSetter;
+import com.fasterxml.jackson.annotation.Nulls;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.exc.InvalidNullException;
+
+// for [databind#1402]; configurable null handling, for values themselves
+public class NullConversionsPojoTest extends BaseMapTest
+{
+    static class NullFail {
+        public String nullsOk = "a";
+
+        @JsonSetter(nulls=Nulls.FAIL)
+        public String noNulls = "b";
+    }
+
+    static class NullAsEmpty {
+        public String nullsOk = "a";
+
+        @JsonSetter(nulls=Nulls.AS_EMPTY)
+        public String nullAsEmpty = "b";
+    }
+
+    static class NullsForString {
+        /*
+        String n = "foo";
+
+        public void setName(String name) {
+            n = name;
+        }
+        */
+
+        String n = "foo";
+
+        public void setName(String n0) { n = n0; }
+        public String getName() { return n; }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = newObjectMapper();
+
+    public void testFailOnNull() throws Exception
+    {
+        // first, ok if assigning non-null to not-nullable, null for nullable
+        NullFail result = MAPPER.readValue(aposToQuotes("{'noNulls':'foo', 'nullsOk':null}"),
+                NullFail.class);
+        assertEquals("foo", result.noNulls);
+        assertNull(result.nullsOk);
+
+        // and then see that nulls are not ok for non-nullable
+        try {
+            result = MAPPER.readValue(aposToQuotes("{'noNulls':null}"),
+                    NullFail.class);
+            fail("Should not pass");
+        } catch (InvalidNullException e) {
+            verifyException(e, "property \"noNulls\"");
+        }
+    }
+
+    public void testFailOnNullWithDefaults() throws Exception
+    {
+        // also: config overrides by type should work
+        String json = aposToQuotes("{'name':null}");
+        NullsForString def = MAPPER.readValue(json, NullsForString.class);
+        assertNull(def.getName());
+        
+        ObjectMapper mapper = newObjectMapper();
+        mapper.configOverride(String.class)
+            .setSetterInfo(JsonSetter.Value.forValueNulls(Nulls.FAIL));
+        try {
+            mapper.readValue(json, NullsForString.class);
+            fail("Should not pass");
+        } catch (InvalidNullException e) {
+            verifyException(e, "property \"name\"");
+        }
+    }
+
+    public void testNullsToEmptyScalar() throws Exception
+    {
+        NullAsEmpty result = MAPPER.readValue(aposToQuotes("{'nullAsEmpty':'foo', 'nullsOk':null}"),
+                NullAsEmpty.class);
+        assertEquals("foo", result.nullAsEmpty);
+        assertNull(result.nullsOk);
+
+        // and then see that nulls are not ok for non-nullable
+        result = MAPPER.readValue(aposToQuotes("{'nullAsEmpty':null}"),
+                NullAsEmpty.class);
+        assertEquals("", result.nullAsEmpty);
+
+        // also: config overrides by type should work
+        String json = aposToQuotes("{'name':null}");
+        NullsForString def = MAPPER.readValue(json, NullsForString.class);
+        assertNull(def.getName());
+
+        ObjectMapper mapper = newObjectMapper();
+        mapper.configOverride(String.class)
+            .setSetterInfo(JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY));
+        NullsForString named = mapper.readValue(json, NullsForString.class);
+        assertEquals("", named.getName());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/filter/NullConversionsSkipTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/filter/NullConversionsSkipTest.java
new file mode 100644
index 0000000..9fe93a3
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/filter/NullConversionsSkipTest.java
@@ -0,0 +1,112 @@
+package com.fasterxml.jackson.databind.deser.filter;
+
+import com.fasterxml.jackson.annotation.JsonSetter;
+import com.fasterxml.jackson.annotation.Nulls;
+import com.fasterxml.jackson.databind.*;
+
+// for [databind#1402]; configurable null handling, specifically with SKIP
+public class NullConversionsSkipTest extends BaseMapTest
+{
+    static class NullSkipField {
+        public String nullsOk = "a";
+
+        @JsonSetter(nulls=Nulls.SKIP)
+        public String noNulls = "b";
+    }
+
+    static class NullSkipMethod {
+        String _nullsOk = "a";
+        String _noNulls = "b";
+
+        public void setNullsOk(String v) {
+            _nullsOk = v;
+        }
+
+        @JsonSetter(nulls=Nulls.SKIP)
+        public void setNoNulls(String v) {
+            _noNulls = v;
+        }
+    }
+    
+    static class StringValue {
+        String value = "default";
+
+        public void setValue(String v) {
+            value = v;
+        }
+    }
+
+    // for [databind#2015]
+    enum NUMS2015 {
+        ONE, TWO
+    }
+
+    public static class Pojo2015 {
+        @JsonSetter(value = "number", nulls = Nulls.SKIP)
+        NUMS2015 number = NUMS2015.TWO;
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods, straight annotation
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = newObjectMapper();
+
+    public void testSkipNullField() throws Exception
+    {
+        // first, ok if assigning non-null to not-nullable, null for nullable
+        NullSkipField result = MAPPER.readValue(aposToQuotes("{'noNulls':'foo', 'nullsOk':null}"),
+                NullSkipField.class);
+        assertEquals("foo", result.noNulls);
+        assertNull(result.nullsOk);
+
+        // and then see that nulls are not ok for non-nullable
+        result = MAPPER.readValue(aposToQuotes("{'noNulls':null}"),
+                NullSkipField.class);
+        assertEquals("b", result.noNulls);
+        assertEquals("a", result.nullsOk);
+    }
+
+    public void testSkipNullMethod() throws Exception
+    {
+        NullSkipMethod result = MAPPER.readValue(aposToQuotes("{'noNulls':'foo', 'nullsOk':null}"),
+                NullSkipMethod.class);
+        assertEquals("foo", result._noNulls);
+        assertNull(result._nullsOk);
+
+        result = MAPPER.readValue(aposToQuotes("{'noNulls':null}"),
+                NullSkipMethod.class);
+        assertEquals("b", result._noNulls);
+        assertEquals("a", result._nullsOk);
+    }
+
+    // for [databind#2015]
+    public void testEnumAsNullThenSkip() throws Exception
+    {    
+        Pojo2015 p = MAPPER.readerFor(Pojo2015.class)
+                .with(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)
+                .readValue("{\"number\":\"THREE\"}"); 
+        assertEquals(NUMS2015.TWO, p.number);
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods, defaulting
+    /**********************************************************
+     */
+    
+    public void testSkipNullWithDefaults() throws Exception
+    {
+        String json = aposToQuotes("{'value':null}");
+        StringValue result = MAPPER.readValue(json, StringValue.class);
+        assertNull(result.value);
+
+        ObjectMapper mapper = newObjectMapper();
+        mapper.configOverride(String.class)
+            .setSetterInfo(JsonSetter.Value.forValueNulls(Nulls.SKIP));
+        result = mapper.readValue(json, StringValue.class);
+        assertEquals("default", result.value);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/filter/ProblemHandlerLocation1440Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/filter/ProblemHandlerLocation1440Test.java
new file mode 100644
index 0000000..14464e7
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/filter/ProblemHandlerLocation1440Test.java
@@ -0,0 +1,141 @@
+package com.fasterxml.jackson.databind.deser.filter;
+
+import java.io.IOException;
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler;
+
+// Test(s) to verify [databind#1440]
+public class ProblemHandlerLocation1440Test extends BaseMapTest
+{
+    static class DeserializationProblem {
+        public List<String> unknownProperties = new ArrayList<>();
+
+        public DeserializationProblem() { }
+
+        public void addUnknownProperty(final String prop) {
+            unknownProperties.add(prop);
+        }
+        public boolean foundProblems() {
+            return !unknownProperties.isEmpty();
+        }
+
+        @Override
+        public String toString() {
+            return "DeserializationProblem{" +"unknownProperties=" + unknownProperties +'}';
+        }
+    }
+
+    static class DeserializationProblemLogger extends DeserializationProblemHandler {
+
+        public DeserializationProblem probs = new DeserializationProblem();
+
+        public List<String> problems() {
+            return probs.unknownProperties;
+        }
+        
+        @Override
+        public boolean handleUnknownProperty(final DeserializationContext ctxt, final JsonParser p,
+                JsonDeserializer<?> deserializer, Object beanOrClass, String propertyName)
+                        throws IOException
+        {
+            final JsonStreamContext parsingContext = p.getParsingContext();
+            final List<String> pathList = new ArrayList<>();
+            addParent(parsingContext, pathList);
+            Collections.reverse(pathList);
+            final String path = _join(".", pathList) + "#" + propertyName;
+
+            probs.addUnknownProperty(path);
+
+            p.skipChildren();
+            return true;
+        }
+
+        static String _join(String sep, Collection<String> parts) {
+            StringBuilder sb = new StringBuilder();
+            for (String part : parts) {
+                if (sb.length() > 0) {
+                    sb.append(sep);
+                }
+                sb.append(part);
+            }
+            return sb.toString();
+        }
+
+        private void addParent(final JsonStreamContext streamContext, final List<String> pathList) {
+            if (streamContext != null && streamContext.getCurrentName() != null) {
+                pathList.add(streamContext.getCurrentName());
+                addParent(streamContext.getParent(), pathList);
+            }
+        }
+    }
+
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    static class Activity {
+        public ActivityEntity actor;
+        public String verb;
+        public ActivityEntity object;
+        public ActivityEntity target;
+
+        @JsonCreator
+        public Activity(@JsonProperty("actor") final ActivityEntity actor, @JsonProperty("object") final ActivityEntity object, @JsonProperty("target") final ActivityEntity target, @JsonProperty("verb") final String verb) {
+            this.actor = actor;
+            this.verb = verb;
+            this.object = object;
+            this.target = target;
+        }
+    }
+
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    static class ActivityEntity {
+        public String id;
+        public String type;
+        public String status;
+        public String context;
+
+        @JsonCreator
+        public ActivityEntity(@JsonProperty("id") final String id, @JsonProperty("type") final String type, @JsonProperty("status") final String status, @JsonProperty("context") final String context) {
+            this.id = id;
+            this.type = type;
+            this.status = status;
+            this.context = context;
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    public void testIncorrectContext() throws Exception
+    {
+        // need invalid to trigger problem:
+        final String invalidInput = aposToQuotes(
+"{'actor': {'id': 'actor_id','type': 'actor_type',"
++"'status': 'actor_status','context':'actor_context','invalid_1': 'actor_invalid_1'},"
++"'verb': 'verb','object': {'id': 'object_id','type': 'object_type',"
++"'invalid_2': 'object_invalid_2','status': 'object_status','context': 'object_context'},"
++"'target': {'id': 'target_id','type': 'target_type','invalid_3': 'target_invalid_3',"
++"'invalid_4': 'target_invalid_4','status': 'target_status','context': 'target_context'}}"
+);
+
+        ObjectMapper mapper = newObjectMapper();
+        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+        final DeserializationProblemLogger logger = new DeserializationProblemLogger();
+        mapper.addHandler(logger);
+        mapper.readValue(invalidInput, Activity.class);
+
+        List<String> probs = logger.problems();
+        assertEquals(4, probs.size());
+        assertEquals("actor.invalid_1#invalid_1", probs.get(0));
+        assertEquals("object.invalid_2#invalid_2", probs.get(1));
+        assertEquals("target.invalid_3#invalid_3", probs.get(2));
+        assertEquals("target.invalid_4#invalid_4", probs.get(3));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/filter/ProblemHandlerTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/filter/ProblemHandlerTest.java
similarity index 77%
rename from src/test/java/com/fasterxml/jackson/databind/filter/ProblemHandlerTest.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/filter/ProblemHandlerTest.java
index 1a9f396..0bd73f5 100644
--- a/src/test/java/com/fasterxml/jackson/databind/filter/ProblemHandlerTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/filter/ProblemHandlerTest.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.filter;
+package com.fasterxml.jackson.databind.deser.filter;
 
 import java.io.IOException;
 import java.util.Map;
@@ -9,6 +9,8 @@
 import com.fasterxml.jackson.core.JsonToken;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler;
+import com.fasterxml.jackson.databind.deser.ValueInstantiator;
+import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
 import com.fasterxml.jackson.databind.exc.InvalidTypeIdException;
 import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
 
@@ -96,6 +98,9 @@
                 Class<?> instClass, Object argument, Throwable t)
             throws IOException
         {
+            if (!(t instanceof InvalidDefinitionException)) {
+                throw new IllegalArgumentException("Should have gotten `InvalidDefinitionException`, instead got: "+t);
+            }
             return value;
         }
     }
@@ -108,10 +113,10 @@
         public MissingInstantiationHandler(Object v0) {
             value = v0;
         }
-    
+
         @Override
         public Object handleMissingInstantiator(DeserializationContext ctxt,
-                Class<?> instClass, JsonParser p, String msg)
+                Class<?> instClass, ValueInstantiator inst, JsonParser p, String msg)
             throws IOException
         {
             p.skipChildren();
@@ -123,11 +128,11 @@
         extends DeserializationProblemHandler
     {
         protected final Object value;
-    
+
         public WeirdTokenHandler(Object v) {
             value = v;
         }
-    
+
         @Override
         public Object handleUnexpectedToken(DeserializationContext ctxt,
                 Class<?> targetType, JsonToken t, JsonParser p,
@@ -138,12 +143,12 @@
         }
     }
 
-    static class TypeIdHandler
+    static class UnknownTypeIdHandler
         extends DeserializationProblemHandler
     {
         protected final Class<?> raw;
 
-        public TypeIdHandler(Class<?> r) { raw = r; }
+        public UnknownTypeIdHandler(Class<?> r) { raw = r; }
         
         @Override
         public JavaType handleUnknownTypeId(DeserializationContext ctxt,
@@ -155,6 +160,23 @@
         }
     }
 
+    static class MissingTypeIdHandler
+        extends DeserializationProblemHandler
+    {
+        protected final Class<?> raw;
+    
+        public MissingTypeIdHandler(Class<?> r) { raw = r; }
+        
+        @Override
+        public JavaType handleMissingTypeId(DeserializationContext ctxt,
+                JavaType baseType, TypeIdResolver idResolver,
+                String failureMsg)
+            throws IOException
+        {
+            return ctxt.constructType(raw);
+        }
+    }
+
     /*
     /**********************************************************
     /* Other helper types
@@ -193,7 +215,7 @@
         public final static BustedCtor INST = new BustedCtor(true);
 
         public BustedCtor() {
-            throw new RuntimeException("Fail!");
+            throw new RuntimeException("Fail! (to be caught by handler)");
         }
         private BustedCtor(boolean b) { }
     }
@@ -210,11 +232,11 @@
     /**********************************************************
      */
 
-    private final ObjectMapper MAPPER = new ObjectMapper();
+    private final ObjectMapper MAPPER = newObjectMapper();
 
     public void testWeirdKeyHandling() throws Exception
     {
-        ObjectMapper mapper = new ObjectMapper()
+        ObjectMapper mapper = newObjectMapper()
             .addHandler(new WeirdKeyHandler(7));
         IntKeyMapWrapper w = mapper.readValue("{\"stuff\":{\"foo\":\"abc\"}}",
                 IntKeyMapWrapper.class);
@@ -226,7 +248,7 @@
 
     public void testWeirdNumberHandling() throws Exception
     {
-        ObjectMapper mapper = new ObjectMapper()
+        ObjectMapper mapper = newObjectMapper()
             .addHandler(new WeirdNumberHandler(SingleValuedEnum.A))
             ;
         SingleValuedEnum result = mapper.readValue("3", SingleValuedEnum.class);
@@ -235,7 +257,7 @@
 
     public void testWeirdStringHandling() throws Exception
     {
-        ObjectMapper mapper = new ObjectMapper()
+        ObjectMapper mapper = newObjectMapper()
             .addHandler(new WeirdStringHandler(SingleValuedEnum.A))
             ;
         SingleValuedEnum result = mapper.readValue("\"B\"", SingleValuedEnum.class);
@@ -250,25 +272,46 @@
 
     public void testInvalidTypeId() throws Exception
     {
-        ObjectMapper mapper = new ObjectMapper()
-            .addHandler(new TypeIdHandler(BaseImpl.class));
+        ObjectMapper mapper = newObjectMapper()
+            .addHandler(new UnknownTypeIdHandler(BaseImpl.class));
         BaseWrapper w = mapper.readValue("{\"value\":{\"type\":\"foo\",\"a\":4}}",
                 BaseWrapper.class);
         assertNotNull(w);
         assertEquals(BaseImpl.class, w.value.getClass());
     }
 
-
     public void testInvalidClassAsId() throws Exception
     {
-        ObjectMapper mapper = new ObjectMapper()
-            .addHandler(new TypeIdHandler(Base2Impl.class));
+        ObjectMapper mapper = newObjectMapper()
+            .addHandler(new UnknownTypeIdHandler(Base2Impl.class));
         Base2Wrapper w = mapper.readValue("{\"value\":{\"clazz\":\"com.fizz\",\"a\":4}}",
                 Base2Wrapper.class);
         assertNotNull(w);
         assertEquals(Base2Impl.class, w.value.getClass());
     }
 
+    // 2.9: missing type id, distinct from unknown
+
+    public void testMissingTypeId() throws Exception
+    {
+        ObjectMapper mapper = newObjectMapper()
+            .addHandler(new MissingTypeIdHandler(BaseImpl.class));
+        BaseWrapper w = mapper.readValue("{\"value\":{\"a\":4}}",
+                BaseWrapper.class);
+        assertNotNull(w);
+        assertEquals(BaseImpl.class, w.value.getClass());
+    }
+
+    public void testMissingClassAsId() throws Exception
+    {
+        ObjectMapper mapper = newObjectMapper()
+            .addHandler(new MissingTypeIdHandler(Base2Impl.class));
+        Base2Wrapper w = mapper.readValue("{\"value\":{\"a\":4}}",
+                Base2Wrapper.class);
+        assertNotNull(w);
+        assertEquals(Base2Impl.class, w.value.getClass());
+    }
+    
     // verify that by default we get special exception type
     public void testInvalidTypeIdFail() throws Exception
     {
@@ -285,7 +328,7 @@
 
     public void testInstantiationExceptionHandling() throws Exception
     {
-        ObjectMapper mapper = new ObjectMapper()
+        ObjectMapper mapper = newObjectMapper()
             .addHandler(new InstantiationProblemHandler(BustedCtor.INST));
         BustedCtor w = mapper.readValue("{ }",
                 BustedCtor.class);
@@ -294,7 +337,7 @@
 
     public void testMissingInstantiatorHandling() throws Exception
     {
-        ObjectMapper mapper = new ObjectMapper()
+        ObjectMapper mapper = newObjectMapper()
             .addHandler(new MissingInstantiationHandler(new NoDefaultCtor(13)))
             ;
         NoDefaultCtor w = mapper.readValue("{ \"x\" : true }", NoDefaultCtor.class);
@@ -304,7 +347,7 @@
 
     public void testUnexpectedTokenHandling() throws Exception
     {
-        ObjectMapper mapper = new ObjectMapper()
+        ObjectMapper mapper = newObjectMapper()
             .addHandler(new WeirdTokenHandler(Integer.valueOf(13)))
         ;
         Integer v = mapper.readValue("true", Integer.class);
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/filter/ReadOnlyDeser1890Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/filter/ReadOnlyDeser1890Test.java
new file mode 100644
index 0000000..0756a99
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/filter/ReadOnlyDeser1890Test.java
@@ -0,0 +1,95 @@
+package com.fasterxml.jackson.databind.deser.filter;
+
+import java.beans.ConstructorProperties;
+import java.io.IOException;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class ReadOnlyDeser1890Test
+    extends BaseMapTest
+{
+    public static class PersonAnnotations {
+        public String name;
+        @JsonProperty(access = JsonProperty.Access.READ_ONLY)
+        private TestEnum testEnum = TestEnum.DEFAULT;
+
+        PersonAnnotations() { }
+
+        @ConstructorProperties({"testEnum", "name"})
+        public PersonAnnotations(TestEnum testEnum, String name) {
+            this.testEnum = testEnum;
+            this.name = name;
+        }
+
+        public TestEnum getTestEnum() {
+            return testEnum;
+        }
+
+        public void setTestEnum(TestEnum testEnum) {
+            this.testEnum = testEnum;
+        }
+   }
+
+    public static class Person {
+        public String name;
+        @JsonProperty(access = JsonProperty.Access.READ_ONLY)
+        private TestEnum testEnum = TestEnum.DEFAULT;
+
+        Person() { }
+
+        public Person(TestEnum testEnum, String name) {
+            this.testEnum = testEnum;
+            this.name = name;
+        }
+
+        public TestEnum getTestEnum() {
+            return testEnum;
+        }
+
+        public void setTestEnum(TestEnum testEnum) {
+            this.testEnum = testEnum;
+        }
+   }
+
+   enum TestEnum{
+       DEFAULT, TEST;
+   }
+
+   /*
+   /**********************************************************
+   /* Test methods
+   /**********************************************************
+    */
+
+   private final ObjectMapper MAPPER = objectMapper();
+
+   public void testDeserializeAnnotationsOneField() throws IOException {
+       PersonAnnotations person = MAPPER.readValue("{\"testEnum\":\"\"}", PersonAnnotations.class);
+       // can not remain as is, so becomes `null`
+       assertEquals(null, person.getTestEnum());
+       assertNull(person.name);
+   }
+
+   public void testDeserializeAnnotationsTwoFields() throws IOException {
+       PersonAnnotations person = MAPPER.readValue("{\"testEnum\":\"\",\"name\":\"changyong\"}",
+               PersonAnnotations.class);
+       // can not remain as is, so becomes `null`
+       assertEquals(null, person.getTestEnum());
+       assertEquals("changyong", person.name);
+   }
+
+   public void testDeserializeOneField() throws IOException {
+       Person person = MAPPER.readValue("{\"testEnum\":\"\"}", Person.class);
+       assertEquals(TestEnum.DEFAULT, person.getTestEnum());
+       assertNull(person.name);
+   }
+
+   public void testDeserializeTwoFields() throws IOException {
+       Person person = MAPPER.readValue("{\"testEnum\":\"\",\"name\":\"changyong\"}",
+               Person.class);
+       assertEquals(TestEnum.DEFAULT, person.getTestEnum());
+       assertEquals("changyong", person.name);
+   }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/filter/ReadOnlyProperties95Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/filter/ReadOnlyDeser95Test.java
similarity index 87%
rename from src/test/java/com/fasterxml/jackson/databind/filter/ReadOnlyProperties95Test.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/filter/ReadOnlyDeser95Test.java
index a00b5c6..2462141 100644
--- a/src/test/java/com/fasterxml/jackson/databind/filter/ReadOnlyProperties95Test.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/filter/ReadOnlyDeser95Test.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.filter;
+package com.fasterxml.jackson.databind.deser.filter;
 
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import com.fasterxml.jackson.databind.*;
@@ -6,7 +6,7 @@
 /**
  * Failing test related to [databind#95]
  */
-public class ReadOnlyProperties95Test extends BaseMapTest
+public class ReadOnlyDeser95Test extends BaseMapTest
 {
     @JsonIgnoreProperties(value={ "computed" }, allowGetters=true)
     static class ReadOnlyBean
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/filter/RecursiveIgnorePropertiesTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/filter/RecursiveIgnorePropertiesTest.java
new file mode 100644
index 0000000..e3a2c7f
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/filter/RecursiveIgnorePropertiesTest.java
@@ -0,0 +1,70 @@
+package com.fasterxml.jackson.databind.deser.filter;
+
+import java.util.Set;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import com.fasterxml.jackson.databind.*;
+
+public class RecursiveIgnorePropertiesTest extends BaseMapTest
+{
+    static class Person {
+        public String name;
+
+        @JsonProperty("person_z") // renaming this to person_p works
+        @JsonIgnoreProperties({"person_z"}) // renaming this to person_p works
+        public Person personZ;
+    }
+
+    static class Persons {
+        public String name;
+
+        @JsonProperty("person_z") // renaming this to person_p works
+        @JsonIgnoreProperties({"person_z"}) // renaming this to person_p works
+        public Set<Persons> personZ;
+    }
+
+    /*
+    /**********************************************************************
+    /* Test methods
+    /**********************************************************************
+     */
+
+    private final ObjectMapper MAPPER = newObjectMapper();
+
+    public void testRecursiveForDeser() throws Exception
+    {
+        String st = aposToQuotes("{ 'name': 'admin',\n"
+                + "    'person_z': { 'name': 'wyatt' }"
+                + "}");
+        Person result = MAPPER.readValue(st, Person.class);
+        assertEquals("admin", result.name);
+        assertNotNull(result.personZ);
+        assertEquals("wyatt", result.personZ.name);
+    }
+
+    public void testRecursiveWithCollectionDeser() throws Exception
+    {
+        String st = aposToQuotes("{ 'name': 'admin',\n"
+                + "    'person_z': [ { 'name': 'Foor' }, { 'name' : 'Bar' } ]"
+                + "}");
+        Persons result = MAPPER.readValue(st, Persons.class);
+        assertEquals("admin", result.name);
+        assertNotNull(result.personZ);
+        assertEquals(2, result.personZ.size());
+    }
+
+    public void testRecursiveForSer() throws Exception
+    {
+        Person input = new Person();
+        input.name = "Bob";
+        Person p2 = new Person();
+        p2.name = "Bill";
+        input.personZ = p2;
+        p2.personZ = input;
+
+        String json = MAPPER.writeValueAsString(input);
+        assertNotNull(json);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/filter/TestUnknownPropertyDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/deser/filter/TestUnknownPropertyDeserialization.java
similarity index 96%
rename from src/test/java/com/fasterxml/jackson/databind/filter/TestUnknownPropertyDeserialization.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/filter/TestUnknownPropertyDeserialization.java
index b362822..dacf423 100644
--- a/src/test/java/com/fasterxml/jackson/databind/filter/TestUnknownPropertyDeserialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/filter/TestUnknownPropertyDeserialization.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.filter;
+package com.fasterxml.jackson.databind.deser.filter;
 
 import java.io.*;
 import java.util.*;
@@ -125,7 +125,7 @@
     /**********************************************************
      */
 
-    private final ObjectMapper MAPPER = new ObjectMapper();
+    private final ObjectMapper MAPPER = newObjectMapper();
 
     /**
      * By default we should just get an exception if an unknown property
@@ -146,7 +146,7 @@
      */
     public void testUnknownHandlingIgnoreWithHandler() throws Exception
     {
-        ObjectMapper mapper = new ObjectMapper();
+        ObjectMapper mapper = newObjectMapper();
         mapper.clearProblemHandlers();
         mapper.addHandler(new MyHandler());
         TestBean result = mapper.readValue(new StringReader(JSON_UNKNOWN_FIELD), TestBean.class);
@@ -162,7 +162,7 @@
      */
     public void testUnknownHandlingIgnoreWithHandlerAndObjectReader() throws Exception
     {
-        ObjectMapper mapper = new ObjectMapper();
+        ObjectMapper mapper = newObjectMapper();
         mapper.clearProblemHandlers();
         TestBean result = mapper.readerFor(TestBean.class).withHandler(new MyHandler()).readValue(new StringReader(JSON_UNKNOWN_FIELD));
         assertNotNull(result);
@@ -177,7 +177,7 @@
      */
     public void testUnknownHandlingIgnoreWithFeature() throws Exception
     {
-        ObjectMapper mapper = new ObjectMapper();
+        ObjectMapper mapper = newObjectMapper();
         mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
         TestBean result = null;
         try {
@@ -271,7 +271,7 @@
 
     public void testIssue987() throws Exception
     {
-        ObjectMapper jsonMapper = new ObjectMapper();
+        ObjectMapper jsonMapper = newObjectMapper();
         jsonMapper.addHandler(new DeserializationProblemHandler() {
             @Override
             public boolean handleUnknownProperty(DeserializationContext ctxt, JsonParser p, JsonDeserializer<?> deserializer, Object beanOrClass, String propertyName) throws IOException, JsonProcessingException {
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/inject/InvalidInjectionTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/inject/InvalidInjectionTest.java
new file mode 100644
index 0000000..a6e2543
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/inject/InvalidInjectionTest.java
@@ -0,0 +1,45 @@
+package com.fasterxml.jackson.databind.deser.inject;
+
+import com.fasterxml.jackson.annotation.JacksonInject;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
+
+public class InvalidInjectionTest extends BaseMapTest
+{
+    static class BadBean1 {
+        @JacksonInject protected String prop1;
+        @JacksonInject protected String prop2;
+    }
+
+    static class BadBean2 {
+        @JacksonInject("x") protected String prop1;
+        @JacksonInject("x") protected String prop2;
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = newObjectMapper();
+    
+    public void testInvalidDup() throws Exception
+    {
+        try {
+            MAPPER.readValue("{}", BadBean1.class);
+            fail("Should not pass");
+        } catch (InvalidDefinitionException e) {
+            verifyException(e, "Duplicate injectable value");
+        }
+        try {
+            MAPPER.readValue("{}", BadBean2.class);
+            fail("Should not pass");
+        } catch (InvalidDefinitionException e) {
+            verifyException(e, "Duplicate injectable value");
+        }
+    }
+
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestInjectables.java b/src/test/java/com/fasterxml/jackson/databind/deser/inject/TestInjectables.java
similarity index 76%
rename from src/test/java/com/fasterxml/jackson/databind/deser/TestInjectables.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/inject/TestInjectables.java
index 2a1c3ef..49acd07 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestInjectables.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/inject/TestInjectables.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.deser;
+package com.fasterxml.jackson.databind.deser.inject;
 
 import com.fasterxml.jackson.annotation.*;
 import com.fasterxml.jackson.databind.*;
@@ -21,16 +21,6 @@
         public void injectThird(long v) {
             third = v;
         }
-    }    
-
-    static class BadBean1 {
-        @JacksonInject protected String prop1;
-        @JacksonInject protected String prop2;
-    }
-
-    static class BadBean2 {
-        @JacksonInject("x") protected String prop1;
-        @JacksonInject("x") protected String prop2;
     }
 
     static class CtorBean {
@@ -55,22 +45,30 @@
         }
     }
 
-    static class IssueGH471Bean {
+    // [databind#77]
+    static class TransientBean {
+        @JacksonInject("transient")
+        transient Object injected;
+
+        public int value;
+    }
+    
+    static class Bean471 {
 
         protected final Object constructorInjected;
         protected final String constructorValue;
 
         @JacksonInject("field_injected") protected Object fieldInjected;
-        @JsonProperty("field_value")     protected String fieldValue;
+        @JsonProperty("field_value") protected String fieldValue;
 
         protected Object methodInjected;
         protected String methodValue;
 
         public int x;
-        
+
         @JsonCreator
-        private IssueGH471Bean(@JacksonInject("constructor_injected") Object constructorInjected,
-                               @JsonProperty("constructor_value") String constructorValue) {
+        private Bean471(@JacksonInject("constructor_injected") Object constructorInjected,
+                @JsonProperty("constructor_value") String constructorValue) {
             this.constructorInjected = constructorInjected;
             this.constructorValue = constructorValue;
         }
@@ -86,25 +84,17 @@
         }
     }
 
-    // [databind#77]
-    static class TransientBean {
-        @JacksonInject("transient")
-        transient Object injected;
-
-        public int value;
-    }
-    
     /*
     /**********************************************************
     /* Unit tests
     /**********************************************************
      */
 
-    private final ObjectMapper MAPPER = new ObjectMapper();
+    private final ObjectMapper MAPPER = newObjectMapper();
     
     public void testSimple() throws Exception
     {
-        ObjectMapper mapper = new ObjectMapper();
+        ObjectMapper mapper = newObjectMapper();
         mapper.setInjectableValues(new InjectableValues.Std()
             .addValue(String.class, "stuffValue")
             .addValue("myId", "xyz")
@@ -127,7 +117,6 @@
         assertEquals("Bubba", bean.name);
     }
 
-    // [Issue-13]
     public void testTwoInjectablesViaCreator() throws Exception
     {
         CtorBean2 bean = MAPPER.readerFor(CtorBean2.class)
@@ -139,34 +128,22 @@
         assertEquals("Bob", bean.name);
     }
 
-    public void testInvalidDup() throws Exception
-    {
-        try {
-            MAPPER.readValue("{}", BadBean1.class);
-        } catch (Exception e) {
-            verifyException(e, "Duplicate injectable value");
-        }
-        try {
-            MAPPER.readValue("{}", BadBean2.class);
-        } catch (Exception e) {
-            verifyException(e, "Duplicate injectable value");
-        }
-    }
-
-    public void testIssueGH471() throws Exception
+    // [databind#471]
+    public void testIssue471() throws Exception
     {
         final Object constructorInjected = "constructorInjected";
         final Object methodInjected = "methodInjected";
         final Object fieldInjected = "fieldInjected";
 
-        ObjectMapper mapper = new ObjectMapper()
+        ObjectMapper mapper = newObjectMapper()
                         .setInjectableValues(new InjectableValues.Std()
                                 .addValue("constructor_injected", constructorInjected)
                                 .addValue("method_injected", methodInjected)
                                 .addValue("field_injected", fieldInjected));
 
-        IssueGH471Bean bean = mapper.readValue("{\"x\":13,\"constructor_value\":\"constructor\",\"method_value\":\"method\",\"field_value\":\"field\"}",
-                IssueGH471Bean.class);
+        Bean471 bean = mapper.readValue(aposToQuotes(
+"{'x':13,'constructor_value':'constructor','method_value':'method','field_value':'field'}"),
+                Bean471.class);
 
         /* Assert *SAME* instance */
         assertSame(constructorInjected, bean.constructorInjected);
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/Base64DecodingTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/Base64DecodingTest.java
new file mode 100644
index 0000000..9e57066
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/Base64DecodingTest.java
@@ -0,0 +1,50 @@
+package com.fasterxml.jackson.databind.deser.jdk;
+
+import java.nio.charset.StandardCharsets;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.exc.MismatchedInputException;
+
+// Mostly for [databind#1425]; not in optimal place (as it also has
+// tree-access tests), but has to do for now
+public class Base64DecodingTest extends BaseMapTest
+{
+    private final ObjectMapper MAPPER = objectMapper();
+
+    private final byte[] HELLO_BYTES = "hello".getBytes(StandardCharsets.UTF_8);
+    private final String BASE64_HELLO = "aGVsbG8=";
+
+    // for [databind#1425]
+    public void testInvalidBase64() throws Exception
+    {
+        byte[] b = MAPPER.readValue(quote(BASE64_HELLO), byte[].class);
+        assertEquals(HELLO_BYTES, b);
+
+        _testInvalidBase64(MAPPER, BASE64_HELLO+"!");
+        _testInvalidBase64(MAPPER, BASE64_HELLO+"!!");
+    }
+
+    private void _testInvalidBase64(ObjectMapper mapper, String value) throws Exception
+    {
+        // First, use data-binding
+        try {
+            MAPPER.readValue(quote(value), byte[].class);
+            fail("Should not pass");
+        } catch (MismatchedInputException e) {
+            verifyException(e, "Failed to decode");
+            verifyException(e, "as base64");
+            verifyException(e, "Illegal character '!'");
+        }
+
+        // and then tree model
+        JsonNode tree = mapper.readTree(String.format("{\"foo\":\"%s\"}", value));
+        JsonNode nodeValue = tree.get("foo");
+        try {
+            /*byte[] b =*/ nodeValue.binaryValue();
+            fail("Should not pass");
+        } catch (MismatchedInputException e) {
+            verifyException(e, "Cannot access contents of TextNode as binary");
+            verifyException(e, "Illegal character '!'");
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestCollectionDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/CollectionDeserTest.java
similarity index 89%
rename from src/test/java/com/fasterxml/jackson/databind/deser/TestCollectionDeserialization.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/jdk/CollectionDeserTest.java
index e67156d..92e4613 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestCollectionDeserialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/CollectionDeserTest.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.deser;
+package com.fasterxml.jackson.databind.deser.jdk;
 
 import java.io.IOException;
 import java.util.*;
@@ -11,7 +11,7 @@
 import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
 
 @SuppressWarnings("serial")
-public class TestCollectionDeserialization
+public class CollectionDeserTest
     extends BaseMapTest
 {
     enum Key {
@@ -42,7 +42,7 @@
         public XBean(int x) { this.x = x; }
     }
 
-    // [Issue#199]
+    // [databind#199]
     static class ListAsIterable {
         public Iterable<String> values;
     }
@@ -226,7 +226,7 @@
             MAPPER.readValue(OBJECTS_JSON, Key[].class);
             fail("Should not pass");
         } catch (JsonMappingException e) {
-            verifyException(e, "Can not deserialize");
+            verifyException(e, "Cannot deserialize");
             List<JsonMappingException.Reference> refs = e.getPath();
             assertEquals(1, refs.size());
             assertEquals(1, refs.get(0).getIndex());
@@ -236,7 +236,7 @@
             MAPPER.readValue("[ \"xyz\", { } ]", String[].class);
             fail("Should not pass");
         } catch (JsonMappingException e) {
-            verifyException(e, "Can not deserialize");
+            verifyException(e, "Cannot deserialize");
             List<JsonMappingException.Reference> refs = e.getPath();
             assertEquals(1, refs.size());
             assertEquals(1, refs.get(0).getIndex());
@@ -246,7 +246,7 @@
             MAPPER.readValue("{\"keys\":"+OBJECTS_JSON+"}", KeyListBean.class);
             fail("Should not pass");
         } catch (JsonMappingException e) {
-            verifyException(e, "Can not deserialize");
+            verifyException(e, "Cannot deserialize");
             List<JsonMappingException.Reference> refs = e.getPath();
             assertEquals(2, refs.size());
             // Bean has no index, but has name:
@@ -284,22 +284,4 @@
             assertEquals("I want to catch this exception", exc.getMessage());
         }
     }
-
-    // And then a round-trip test for singleton collections
-    public void testSingletonCollections() throws Exception
-    {
-        final TypeReference<?> xbeanListType = new TypeReference<List<XBean>>() { };
-
-        String json = MAPPER.writeValueAsString(Collections.singleton(new XBean(3)));
-        Collection<XBean> result = MAPPER.readValue(json, xbeanListType);
-        assertNotNull(result);
-        assertEquals(1, result.size());
-        assertEquals(3, result.iterator().next().x);
-
-        json = MAPPER.writeValueAsString(Collections.singletonList(new XBean(28)));
-        result = MAPPER.readValue(json, xbeanListType);
-        assertNotNull(result);
-        assertEquals(1, result.size());
-        assertEquals(28, result.iterator().next().x);
-    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/DateDeserializationTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/DateDeserializationTZTest.java
similarity index 72%
rename from src/test/java/com/fasterxml/jackson/databind/deser/DateDeserializationTest.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/jdk/DateDeserializationTZTest.java
index 8a7a0b5..f4cd977 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/DateDeserializationTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/DateDeserializationTZTest.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.deser;
+package com.fasterxml.jackson.databind.deser.jdk;
 
 import java.math.BigInteger;
 import java.text.DateFormat;
@@ -9,20 +9,23 @@
 import java.util.TimeZone;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
-import com.fasterxml.jackson.core.JsonParseException;
 import com.fasterxml.jackson.databind.BaseMapTest;
-import com.fasterxml.jackson.databind.JsonMappingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.exc.InvalidFormatException;
+import com.fasterxml.jackson.databind.exc.MismatchedInputException;
 
+/**
+ * Additional `java.util.Date` deserialization tests for cases where `ObjectMapper`
+ * is configured to use timezone different from UTC.
+ */
 @SuppressWarnings("javadoc")
-public class DateDeserializationTest
+public class DateDeserializationTZTest
     extends BaseMapTest
 {
     private static final String LOCAL_TZ = "GMT+2";
 
     private static final DateFormat FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
-    
+
     static class Annot_TimeZone {
         @JsonFormat(timezone="GMT+4")
         private java.util.Date date;
@@ -94,23 +97,19 @@
         //   According to ISO8601, hours and minutes of the offset must be expressed with 2 digits 
         //   (not more, not less), i.e. Z or +hh:mm or -hh:mm. See https://www.w3.org/TR/NOTE-datetime. 
         //
-        //   The forms below should be refused but some are accepted by the StdDateFormat. They are 
-        //   included in the test to detect any change in behavior in futur releases...
+        //   The forms below are therefore ILLEGAL and must be refused.
         // ---------------------------------------------------------------------------------------------
 
-        // Interpreted as if there was no timezone, therefore producing a date with the TZ set on the mapper
-        // FIXME it is probably better to refuse these cases instead of silently creating dates in local tz...
-        verify( MAPPER, "2000-01-02T03:04:05.678+",        judate(2000, 1, 2,   3, 4, 5, 678, LOCAL_TZ));
-        verify( MAPPER, "2000-01-02T03:04:05.678+1",       judate(2000, 1, 2,   3, 4, 5, 678, LOCAL_TZ));
-            // FIXME this should probably give GMT+1
-        verify( MAPPER, "2000-01-02T03:04:05.678+001",     judate(2000, 1, 2,   3, 4, 5, 678, LOCAL_TZ));
-        verify( MAPPER, "2000-01-02T03:04:05.678+00:",     judate(2000, 1, 2,   3, 4, 5, 678, LOCAL_TZ));
-        verify( MAPPER, "2000-01-02T03:04:05.678+00:001",  judate(2000, 1, 2,   3, 4, 5, 678, LOCAL_TZ));
-        verify( MAPPER, "2000-01-02T03:04:05.678+001:001", judate(2000, 1, 2,   3, 4, 5, 678, LOCAL_TZ));
+        failure( MAPPER, "2000-01-02T03:04:05.678+"); 
+        failure( MAPPER, "2000-01-02T03:04:05.678+1");
 
-        // Considering the above forms have been accepted, it is strange the following are refused...
-        failure( MAPPER, "2000-01-02T03:04:05.678+1:");      // FIXME
-        failure( MAPPER, "2000-01-02T03:04:05.678+00:1");    // FIXME
+        failure( MAPPER, "2000-01-02T03:04:05.678+001"); 
+        failure( MAPPER, "2000-01-02T03:04:05.678+00:");
+        failure( MAPPER, "2000-01-02T03:04:05.678+00:001");
+        failure( MAPPER, "2000-01-02T03:04:05.678+001:001");
+
+        failure( MAPPER, "2000-01-02T03:04:05.678+1:");
+        failure( MAPPER, "2000-01-02T03:04:05.678+00:1");
     }
 
     /**
@@ -119,34 +118,30 @@
     public void testDateUtilISO8601_DateTimeMillis() throws Exception 
     {    
         // WITH timezone (from 4 to 0 digits)
-        failure(MAPPER, "2000-01-02T03:04:05.6789+01:00");
-        verify( MAPPER, "2000-01-02T03:04:05.678+01:00", judate(2000, 1, 2,   3, 4, 5, 678, "GMT+1"));
-        verify( MAPPER, "2000-01-02T03:04:05.67+01:00",  judate(2000, 1, 2,   3, 4, 5, 670, "GMT+1"));
-        verify( MAPPER, "2000-01-02T03:04:05.6+01:00",   judate(2000, 1, 2,   3, 4, 5, 600, "GMT+1"));
-        verify( MAPPER, "2000-01-02T03:04:05+01:00",     judate(2000, 1, 2,   3, 4, 5, 000, "GMT+1"));
+    		failure(MAPPER, "2000-01-02T03:04:05.0123456789+01:00");	// at most 9 digits for the millis
+        verify( MAPPER, "2000-01-02T03:04:05.6789+01:00", judate(2000, 1, 2,   3, 4, 5, 678, "GMT+1"));
+        verify( MAPPER, "2000-01-02T03:04:05.678+01:00",  judate(2000, 1, 2,   3, 4, 5, 678, "GMT+1"));
+        verify( MAPPER, "2000-01-02T03:04:05.67+01:00",   judate(2000, 1, 2,   3, 4, 5, 670, "GMT+1"));
+        verify( MAPPER, "2000-01-02T03:04:05.6+01:00",    judate(2000, 1, 2,   3, 4, 5, 600, "GMT+1"));
+        verify( MAPPER, "2000-01-02T03:04:05+01:00",      judate(2000, 1, 2,   3, 4, 5, 000, "GMT+1"));
 
-        
+
         // WITH timezone Z (from 4 to 0 digits)
-        verify( MAPPER, "2000-01-02T03:04:05.6789Z", judate(2000, 1, 2,   3, 4, 11, 789, "UTC"));
-            // FIXME the .6789 millis are interpreted as 6789 millisecondes or 6.789 seconds!
-        verify( MAPPER, "2000-01-02T03:04:05.678Z", judate(2000, 1, 2,   3, 4, 5, 678, "UTC"));
-        verify( MAPPER, "2000-01-02T03:04:05.67Z",  judate(2000, 1, 2,   3, 4, 5,  67, "UTC"));
-           // FIXME should be 670 millis instead of 67
-        verify( MAPPER, "2000-01-02T03:04:05.6Z",   judate(2000, 1, 2,   3, 4, 5,   6, "UTC"));
-           // FIXME should be 600 millis instead of 6
-        verify( MAPPER, "2000-01-02T03:04:05Z",     judate(2000, 1, 2,   3, 4, 5,   0, "UTC"));
+		failure(MAPPER, "2000-01-02T03:04:05.0123456789Z");	// at most 9 digits for the millis
+        verify( MAPPER, "2000-01-02T03:04:05.6789Z",      judate(2000, 1, 2,   3, 4, 5, 678, "UTC"));
+        verify( MAPPER, "2000-01-02T03:04:05.678Z",       judate(2000, 1, 2,   3, 4, 5, 678, "UTC"));
+        verify( MAPPER, "2000-01-02T03:04:05.67Z",        judate(2000, 1, 2,   3, 4, 5, 670, "UTC"));
+        verify( MAPPER, "2000-01-02T03:04:05.6Z",         judate(2000, 1, 2,   3, 4, 5, 600, "UTC"));
+        verify( MAPPER, "2000-01-02T03:04:05Z",           judate(2000, 1, 2,   3, 4, 5,   0, "UTC"));
         
 
         // WITHOUT timezone (from 4 to 0 digits)
-        verify(MAPPER, "2000-01-02T03:04:05.6789",       judate(2000, 1, 2,   3, 4, 11, 789, LOCAL_TZ));
-            // FIXME: the .6789 millis are interpreted as 6789 millisecondes or 6.789 seconds!
-        
-        verify( MAPPER, "2000-01-02T03:04:05.678",       judate(2000, 1, 2,   3, 4,  5, 678, LOCAL_TZ));
-        verify( MAPPER, "2000-01-02T03:04:05.67",        judate(2000, 1, 2,   3, 5, 12, 000, LOCAL_TZ));
-            // FIXME: the .67 millis are interpreted as 67 seconds.
-        
-        verify( MAPPER, "2000-01-02T03:04:05.6",         judate(2000, 1, 2,   3, 4,  5, 600, LOCAL_TZ));
-        verify( MAPPER, "2000-01-02T03:04:05",           judate(2000, 1, 2,   3, 4,  5, 000, LOCAL_TZ));
+		failure(MAPPER, "2000-01-02T03:04:05.0123456789");	// at most 9 digits for the millis
+        verify( MAPPER, "2000-01-02T03:04:05.6789",       judate(2000, 1, 2,   3, 4,  5, 678, LOCAL_TZ));
+        verify( MAPPER, "2000-01-02T03:04:05.678",        judate(2000, 1, 2,   3, 4,  5, 678, LOCAL_TZ));
+        verify( MAPPER, "2000-01-02T03:04:05.67",         judate(2000, 1, 2,   3, 4,  5, 670, LOCAL_TZ));
+        verify( MAPPER, "2000-01-02T03:04:05.6",          judate(2000, 1, 2,   3, 4,  5, 600, LOCAL_TZ));
+        verify( MAPPER, "2000-01-02T03:04:05",            judate(2000, 1, 2,   3, 4,  5, 000, LOCAL_TZ));
         
         
         // ---------------------------------------------------------------------------------------------
@@ -161,17 +156,16 @@
         //      time-secfrac    = "." 1*DIGIT
         //      partial-time    = time-hour ":" time-minute ":" time-second [time-secfrac]
         //
-        //   The second fraction (ie the millis) is optional and can be ommitted. However, a fraction
+        //   The second fraction (ie the millis) is optional and can be omitted. However, a fraction
         //   with only a dot (.) and no digit is not allowed.
         //
-        //   The forms below should be refused but some are accepted by the StdDateFormat. They are 
-           //   included in the test to detect any change in behavior in futur releases...
+        //   The forms below are therefore ILLEGAL and must be refused.
         // ---------------------------------------------------------------------------------------------
         
         // millis part with only a dot (.) and no digits
-        verify( MAPPER, "2000-01-02T03:04:05.+01:00",    judate(2000, 1, 2,   3, 4, 5, 000, "GMT+1"));
-        verify( MAPPER, "2000-01-02T03:04:05.",          judate(2000, 1, 2,   3, 4, 5, 000, LOCAL_TZ));
-        failure(MAPPER, "2000-01-02T03:04:05.Z");	     // FIXME this one fails, but not the others...
+        failure( MAPPER, "2000-01-02T03:04:05.+01:00");
+        failure( MAPPER, "2000-01-02T03:04:05.");
+        failure( MAPPER, "2000-01-02T03:04:05.Z");
     }
 
 
@@ -188,25 +182,24 @@
         // No timezone --> the one configured on the ObjectMapper must be used
         verify(MAPPER, "2000-01-02T03:04:05",        judate(2000, 1, 2,   3, 4, 5, 0, LOCAL_TZ));
 
-        // Hours, minutes and seconds are mandatory when time is specified
+        // Hours, minutes are mandatory but seconds are optional
         failure(MAPPER, "2000-01-02T");
         failure(MAPPER, "2000-01-02T03");
         failure(MAPPER, "2000-01-02T03:");
-        failure(MAPPER, "2000-01-02T03:04");
+        verify(MAPPER, "2000-01-02T03:04", judate(2000, 1, 2,  3, 4, 0, 0, LOCAL_TZ));
         failure(MAPPER, "2000-01-02T03:04:");
-
-        // Although hours, minutes and seconds are mandatory, they can sometimes be omitted 
-        // if a TZ is specified... !!??
+        
+        // Hours, minutes are mandatory but seconds are optional - test with a TZ
         failure(MAPPER, "2000-01-02T+01:00");
         failure(MAPPER, "2000-01-02T03+01:00");
         failure(MAPPER, "2000-01-02T03:+01:00");
-        verify( MAPPER, "2000-01-02T03:04+01:00",   judate(2000, 1, 2,   3, 4, 0, 0, "GMT+1"));    // FIXME should be refused
+        verify( MAPPER, "2000-01-02T03:04+01:00", judate(2000, 1, 2,   3, 4, 0, 0, "GMT+1"));
         failure(MAPPER, "2000-01-02T03:04:+01:00");
         
         failure(MAPPER, "2000-01-02TZ");
         failure(MAPPER, "2000-01-02T03Z");
         failure(MAPPER, "2000-01-02T03:Z");
-        failure(MAPPER, "2000-01-02T03:04Z");
+        verify(MAPPER, "2000-01-02T03:04Z", judate(2000, 1, 2,  3, 4, 0, 0, "UTC"));
         failure(MAPPER, "2000-01-02T03:04:Z");
 
         
@@ -228,8 +221,8 @@
         // Behavior should be the SAME whatever the timezone and/or the millis.
         
         // seconds (no TZ)
-        verify( MAPPER, "2000-01-02T03:04:5",           judate(2000, 1, 2,   3, 4, 5, 0, LOCAL_TZ));
-        verify( MAPPER, "2000-01-02T03:04:5.000",       judate(2000, 1, 2,   3, 4, 5, 0, LOCAL_TZ));
+        failure( MAPPER, "2000-01-02T03:04:5");
+        failure( MAPPER, "2000-01-02T03:04:5.000");
         failure(MAPPER, "2000-01-02T03:04:005");
         
         // seconds (+01:00)
@@ -239,13 +232,13 @@
         
         // seconds (Z)
         failure(MAPPER, "2000-01-02T03:04:5Z");
-        verify( MAPPER, "2000-01-02T03:04:5.000Z",      judate(2000, 1, 2,   3, 4, 5, 0, "UTC"));
+        failure( MAPPER, "2000-01-02T03:04:5.000Z");
         failure(MAPPER, "2000-01-02T03:04:005Z");
         
 
         // minutes (no TZ)
-        verify( MAPPER, "2000-01-02T03:4:05",           judate(2000, 1, 2,   3, 4, 5, 0, LOCAL_TZ));
-        verify( MAPPER, "2000-01-02T03:4:05.000",       judate(2000, 1, 2,   3, 4, 5, 0, LOCAL_TZ));
+        failure( MAPPER, "2000-01-02T03:4:05");
+        failure( MAPPER, "2000-01-02T03:4:05.000");
         failure(MAPPER, "2000-01-02T03:004:05");
         
         // minutes (+01:00)
@@ -254,14 +247,13 @@
         failure(MAPPER, "2000-01-02T03:004:05+01:00");
         
         // minutes (Z)
-        verify( MAPPER, "2000-01-02T03:4:05Z",          judate(2000, 1, 2,   3, 4, 5, 0, "UTC"));
-        verify( MAPPER, "2000-01-02T03:4:05.000Z",      judate(2000, 1, 2,   3, 4, 5, 0, "UTC"));
-        verify( MAPPER, "2000-01-02T03:004:05Z",        judate(2000, 1, 2,   3, 4, 5, 0, "UTC"));
-
+        failure( MAPPER, "2000-01-02T03:4:05Z");
+        failure( MAPPER, "2000-01-02T03:4:05.000Z");
+        failure( MAPPER, "2000-01-02T03:004:05Z");
 
         // hour (no TZ)
-        verify( MAPPER, "2000-01-02T3:04:05",           judate(2000, 1, 2,   3, 4, 5, 0, LOCAL_TZ));
-        verify( MAPPER, "2000-01-02T3:04:05.000",       judate(2000, 1, 2,   3, 4, 5, 0, LOCAL_TZ));
+        failure( MAPPER, "2000-01-02T3:04:05");
+        failure( MAPPER, "2000-01-02T3:04:05.000");
         failure(MAPPER, "2000-01-02T003:04:05");
 
         // hour (+01:00)
@@ -270,12 +262,11 @@
         failure(MAPPER, "2000-01-02T003:04:05+01:00");
 
         // hour (Z)
-        verify( MAPPER, "2000-01-02T3:04:05Z",         judate(2000, 1, 2,   3, 4, 5, 0, "UTC"));
-        verify( MAPPER, "2000-01-02T3:04:05.000Z",     judate(2000, 1, 2,   3, 4, 5, 0, "UTC"));
-        verify( MAPPER, "2000-01-02T003:04:05Z",       judate(2000, 1, 2,   3, 4, 5, 0, "UTC"));
+        failure( MAPPER, "2000-01-02T3:04:05Z");
+        failure( MAPPER, "2000-01-02T3:04:05.000Z");
+        failure( MAPPER, "2000-01-02T003:04:05Z");
     }
 
-
     /**
      * Date-only representations (no Time part)
      * 
@@ -299,21 +290,20 @@
         // ---------------------------------------------------------------------------------------------
 
         // day
-        verify(  MAPPER, "2000-01-2",      judate(2000, 1, 2,   0, 0, 0, 0, LOCAL_TZ));
+        failure( MAPPER, "2000-01-2"); 
         failure( MAPPER, "2000-01-002");
         
         // month
-        verify(  MAPPER, "2000-1-02",      judate(2000, 1, 2,   0, 0, 0, 0, LOCAL_TZ));
+        failure( MAPPER, "2000-1-02");
         failure( MAPPER, "2000-001-02");
         
         // year
         failure( MAPPER, "20000-01-02");
         failure( MAPPER, "200-01-02"  );
         failure( MAPPER, "20-01-02"   );
-        verify(  MAPPER, "2-01-02",        judate(2, 1, 2,   0, 0, 0, 0, LOCAL_TZ));    // FIXME Why accept 1 digit and refuse they other cases??
+        failure(  MAPPER, "2-01-02");
     }
 
-
     /**
      * DateTime as numeric representation
      */
@@ -325,31 +315,29 @@
             verify( MAPPER, Long.toString(now), new java.util.Date(now) ); // as a string
         }
         {
-            /* As of 1.5.0, should be ok to pass as JSON String, as long
-             * as it is plain timestamp (all numbers, 64-bit)
-             */
+            // should be ok to pass as JSON String, as long
+            // as it is plain timestamp (all numbers, 64-bit)
             long now = 1321992375446L;
-            verify( MAPPER,                now, new java.util.Date(now) );    // as a long
+            verify( MAPPER,                now, new java.util.Date(now) );	// as a long
             verify( MAPPER, Long.toString(now), new java.util.Date(now) );  // as a string
         }
         {
             // #267: should handle negative timestamps too; like 12 hours before 1.1.1970
             long now = - (24 * 3600 * 1000L);
-            verify( MAPPER,                now, new java.util.Date(now) );    // as a long
+            verify( MAPPER,                now, new java.util.Date(now) );	// as a long
             verify( MAPPER, Long.toString(now), new java.util.Date(now) );  // as a string
         }
-
+    	
         // value larger than a long (Long.MAX_VALUE+1)
         BigInteger tooLarge = BigInteger.valueOf(Long.MAX_VALUE).add( BigInteger.valueOf(1) );
-        failure(MAPPER, tooLarge, JsonParseException.class);    // FIXME: InvalidFormatException is thrown everywhere else...
-        failure(MAPPER, tooLarge.toString());
-
+        failure(MAPPER, tooLarge, InvalidFormatException.class);
+        failure(MAPPER, tooLarge.toString(), InvalidFormatException.class);
+    	
         // decimal value
-        failure(MAPPER, 0.0, JsonMappingException.class);        // FIXME: InvalidFormatException is thrown everywhere else...
-        failure(MAPPER, "0.0");
+        failure(MAPPER, 0.0, MismatchedInputException.class);
+        failure(MAPPER, "0.0", InvalidFormatException.class);
     }
 
-
     /**
      * Note: may be these cases are already covered by {@link #testDateUtil_Annotation_PatternAndLocale()}
      */
@@ -525,31 +513,30 @@
         cal.set(year, month-1, day, hour, minutes, seconds);
         cal.set(Calendar.MILLISECOND, millis);
         cal.setTimeZone(TimeZone.getTimeZone(tz));
-        
         return cal.getTime();
     }
 
     private static void verify(ObjectMapper mapper, Object input, Date expected) throws Exception {
-        // Deserialize using the supplied ObjectMapper
-        Date actual = read(mapper, input, java.util.Date.class);
-
-        // Test against the expected
-        if( expected==null && actual==null) {
-            return;
-        }
-        if( expected==null && actual != null) {
-            fail("Failed to deserialize "+input+", actual: '"+FORMAT.format(actual)+"', expected: <null>'");
-        }
-        if( expected != null && actual == null ) {
-            fail("Failed to deserialize "+input+", actual: <null>, expected: '"+FORMAT.format(expected)+"'");
-        }
-        if( actual.getTime() != expected.getTime() ) {
-            fail("Failed to deserialize "+input+", actual: '"+FORMAT.format(actual)+"', expected: '"+FORMAT.format(expected)+"'");
-        }
+		// Deserialize using the supplied ObjectMapper
+		Date actual = read(mapper, input, java.util.Date.class);
+		
+		// Test against the expected
+		if( expected==null && actual==null) {
+			return;
+		}
+		if( expected==null && actual != null) {
+			fail("Failed to deserialize "+input+", actual: '"+FORMAT.format(actual)+"', expected: <null>'");
+		}
+		if( expected != null && actual == null ) {
+			fail("Failed to deserialize "+input+", actual: <null>, expected: '"+FORMAT.format(expected)+"'");
+		}
+		if( actual.getTime() != expected.getTime() ) {
+			fail("Failed to deserialize "+input+", actual: '"+FORMAT.format(actual)+"', expected: '"+FORMAT.format(expected)+"'");
+		}
     }
 
     private static void failure(ObjectMapper mapper, Object input) throws Exception {
-        failure(mapper, input, InvalidFormatException.class);
+        failure(mapper, input, MismatchedInputException.class);
     }
 
     private static void failure(ObjectMapper mapper, Object input, Class<? extends Exception> exceptionType) throws Exception {
@@ -559,7 +546,7 @@
         }
         catch(Exception e) {
             // Is it the expected exception ?
-            if( ! exceptionType.isAssignableFrom(e.getClass()) ) {
+            if (!exceptionType.isAssignableFrom(e.getClass()) ) {
                 fail("Wrong exception thrown when reading "+input+", actual: "+e.getClass().getName() + "("+e.getMessage()+"), expected: "+exceptionType.getName());
             }
         }
@@ -571,7 +558,6 @@
         if( !(input instanceof Number) ) {
             json = "\""+json+"\"";
         }
-
         // Deserialize using the supplied ObjectMapper
         return (T) mapper.readValue(json, type);
     }
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestDateDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/DateDeserializationTest.java
similarity index 68%
rename from src/test/java/com/fasterxml/jackson/databind/deser/TestDateDeserialization.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/jdk/DateDeserializationTest.java
index 3224a61..5ec830e 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestDateDeserialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/DateDeserializationTest.java
@@ -1,18 +1,20 @@
-package com.fasterxml.jackson.databind.deser;
+package com.fasterxml.jackson.databind.deser.jdk;
 
+import java.beans.ConstructorProperties;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
 import java.util.*;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.OptBoolean;
 import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.exc.MismatchedInputException;
 import com.fasterxml.jackson.databind.exc.InvalidFormatException;
-import com.fasterxml.jackson.databind.util.ISO8601DateFormat;
 
-public class TestDateDeserialization
+public class DateDeserializationTest
     extends BaseMapTest
 {
-    // Test for [JACKSON-435]
     static class DateAsStringBean
     {
         @JsonFormat(shape=JsonFormat.Shape.STRING, pattern="/yyyy/MM/dd/")
@@ -35,14 +37,60 @@
         @JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd,HH", timezone="CET")
         public Date date;
     }
+
+    static class CalendarBean {
+        Calendar _v;
+        void setV(Calendar v) { _v = v; }
+    }
+
+    static class LenientCalendarBean {
+        @JsonFormat(lenient=OptBoolean.TRUE)
+        public Calendar value;
+    }
     
+    static class StrictCalendarBean {
+        @JsonFormat(lenient=OptBoolean.FALSE)
+        public Calendar value;
+    }
+
+    // [databind#1722]
+    public static class Date1722 {
+        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS")
+        private Date date;
+
+        @JsonIgnore
+        private String foo;
+
+        @ConstructorProperties({"date", "foo"})
+        public Date1722(Date date, String foo) {
+            this.date = date;
+            this.foo = foo;
+        }
+
+        public Date getDate() {
+            return this.date;
+        }
+
+        public void setDate(Date date) {
+            this.date = date;
+        }
+
+        public String getFoo() {
+            return this.foo;
+        }
+
+        public void setFoo(String foo) {
+            this.foo = foo;
+        }
+    }
+
     /*
     /**********************************************************
     /* Unit tests
     /**********************************************************
      */
 
-    private final ObjectMapper MAPPER = new ObjectMapper();
+    private final ObjectMapper MAPPER = newObjectMapper();
 
     public void testDateUtil() throws Exception
     {
@@ -63,7 +111,7 @@
     public void testDateUtilWithStringTimestamp() throws Exception
     {
         long now = 1321992375446L;
-        /* As of 1.5.0, should be ok to pass as JSON String, as long
+        /* Should be ok to pass as JSON String, as long
          * as it is plain timestamp (all numbers, 64-bit)
          */
         String json = quote(String.valueOf(now));
@@ -208,10 +256,53 @@
         assertEquals(450, c.get(Calendar.MILLISECOND));
     }
 
+    // Also: minutes-part of offset need not be all zeroes: [databind#1788]
+    public void testISO8601FractionalTimezoneOffset() throws Exception
+    {
+        String inputStr = "1997-07-16T19:20:30.45+01:30";
+        java.util.Date inputDate = MAPPER.readValue(quote(inputStr), java.util.Date.class);
+        Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
+        c.setTime(inputDate);
+        assertEquals(1997, c.get(Calendar.YEAR));
+        assertEquals(Calendar.JULY, c.get(Calendar.MONTH));
+        assertEquals(16, c.get(Calendar.DAY_OF_MONTH));
+        assertEquals(19 - 2, c.get(Calendar.HOUR_OF_DAY));
+        assertEquals(50, c.get(Calendar.MINUTE));
+        assertEquals(30, c.get(Calendar.SECOND));
+        assertEquals(450, c.get(Calendar.MILLISECOND));
+    }
+
+    // [databind#1745]
+    public void testISO8601FractSecondsLong() throws Exception
+    {
+        String inputStr;
+        Date inputDate;
+        Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+
+        inputStr = "2014-10-03T18:00:00.3456-05:00";
+        inputDate = MAPPER.readValue(quote(inputStr), java.util.Date.class);
+        c.setTime(inputDate);
+        assertEquals(2014, c.get(Calendar.YEAR));
+        assertEquals(Calendar.OCTOBER, c.get(Calendar.MONTH));
+        assertEquals(3, c.get(Calendar.DAY_OF_MONTH));
+        // should truncate; not error or round
+        assertEquals(345, c.get(Calendar.MILLISECOND));
+
+        // But! Still limit to 9 digits (nanoseconds)
+        try {
+            MAPPER.readValue(quote("2014-10-03T18:00:00.1234567890-05:00"), java.util.Date.class);
+        } catch (InvalidFormatException e) {
+            verifyException(e, "invalid fractional seconds");
+            verifyException(e, "can use at most 9 digits");
+        }
+    }
+
     public void testISO8601MissingSeconds() throws Exception
     {
         String inputStr;
         Date inputDate;
+
+        // 23-Jun-2017, tatu: Shouldn't this be UTC?
         Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
 
         inputStr = "1997-07-16T19:20+01:00";
@@ -285,7 +376,17 @@
         assertEquals(2, c.get(Calendar.HOUR_OF_DAY));
     }
 
-    // [Issue#338]
+    // [databind#1722]: combination of `@ConstructorProperties` and `@JsonIgnore`
+    //  should work fine.
+    public void testFormatAndCtors1722() throws Exception
+    {
+        Date1722 input = new Date1722(new Date(0L), "bogus");
+        String json = MAPPER.writeValueAsString(input);
+        Date1722 result = MAPPER.readValue(json, Date1722.class);
+        assertNotNull(result);
+    }
+
+    // [databind#338]
     public void testDateUtilISO8601NoMilliseconds() throws Exception
     {
         final String INPUT_STR = "2013-10-31T17:27:00";
@@ -306,7 +407,7 @@
         // 03-Nov-2013, tatu: This wouldn't work, and is the nominal reason
         //    for #338 I think
         /*
-        inputDate =  ISO8601Utils.parse(INPUT_STR);
+        inputDate =  ISO8601Utils.parse(INPUT_STR, new ParsePosition(0));
         c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
         c.setTime(inputDate);
         assertEquals(2013, c.get(Calendar.YEAR));
@@ -385,7 +486,10 @@
         result = MAPPER.readValue(quote(dateStr), Calendar.class);
 
         // note: representation may differ (wrt timezone etc), but underlying value must remain the same:
-        assertEquals(l, result.getTimeInMillis());
+        if (l != result.getTimeInMillis()) {
+            fail(String.format("Expected timestamp %d, got %d, for '%s'",
+                    l, result.getTimeInMillis(), dateStr));
+        }
     }
 
     public void testCustom() throws Exception
@@ -412,7 +516,6 @@
         assertNull(MAPPER.readValue(quote(""), java.sql.Date.class));
     }
 
-    // for [JACKSON-334]
     public void test8601DateTimeNoMilliSecs() throws Exception
     {
         // ok, Zebra, no milliseconds
@@ -513,17 +616,146 @@
         assertEquals(9, c.get(Calendar.HOUR_OF_DAY));
     }
 
-    // Based on an external report; was throwing an exception for second case here
-    public void testISO8601Directly() throws Exception
+    // [databind#1651]
+    public void testDateEndingWithZNonDefTZ1651() throws Exception
     {
-        final String TIME_STR = "2015-01-21T08:56:13.533+0000";
-        Date d = MAPPER.readValue(quote(TIME_STR), Date.class);
-        assertNotNull(d);
+        String json = quote("1970-01-01T00:00:00.000Z");
 
-        ISO8601DateFormat f = new ISO8601DateFormat();
-        Date d2 = f.parse(TIME_STR);
-        assertNotNull(d2);
-        assertEquals(d.getTime(), d2.getTime());
+        // Standard mapper with timezone UTC: shared instance should be ok.
+        // ... but, Travis manages to have fails, so insist on newly created
+        ObjectMapper mapper = newObjectMapper();
+        Date dateUTC = mapper.readValue(json, Date.class);  // 1970-01-01T00:00:00.000+00:00
+    
+        // Mapper with timezone GMT-2
+        // note: must construct new one, not share
+        mapper = new ObjectMapper();
+        mapper.setTimeZone(TimeZone.getTimeZone("GMT-2"));
+        Date dateGMT1 = mapper.readValue(json, Date.class);  // 1970-01-01T00:00:00.000-02:00
+    
+        // Underlying timestamps should be the same
+        assertEquals(dateUTC.getTime(), dateGMT1.getTime());
+    }
+
+    /*
+    /**********************************************************
+    /* Context timezone use (or not)
+    /**********************************************************
+     */
+    
+    // for [databind#204]
+    public void testContextTimezone() throws Exception
+    {
+        String inputStr = "1997-07-16T19:20:30.45+0100";
+        final String tzId = "PST";
+
+        // this is enabled by default:
+        assertTrue(MAPPER.isEnabled(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE));
+        final ObjectReader r = MAPPER
+                .readerFor(Calendar.class)
+                .with(TimeZone.getTimeZone(tzId));
+
+        // by default use contextual timezone:
+        Calendar cal = r.readValue(quote(inputStr));
+        TimeZone tz = cal.getTimeZone();
+        assertEquals(tzId, tz.getID());
+
+        assertEquals(1997, cal.get(Calendar.YEAR));
+        assertEquals(Calendar.JULY, cal.get(Calendar.MONTH));
+        assertEquals(16, cal.get(Calendar.DAY_OF_MONTH));
+
+        // Translated from original into PST differs:
+        assertEquals(20, cal.get(Calendar.MINUTE));
+        assertEquals(30, cal.get(Calendar.SECOND));
+        assertEquals(11, cal.get(Calendar.HOUR_OF_DAY));
+
+        // but if disabled, should use what's been sent in:
+        cal = r.without(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)
+                .readValue(quote(inputStr));
+
+        // 23-Jun-2017, tatu: Actually turns out to be hard if not impossible to do ...
+        //    problem being SimpleDateFormat does not really retain timezone offset.
+        //    But if we match fields... we perhaps could use it?
+        
+        // !!! TODO: would not yet pass
+/*
+        System.err.println("CAL/2 == "+cal);
+
+        System.err.println("tz == "+cal.getTimeZone());
+        */
+    }
+
+    /*
+    /**********************************************************
+    /* Test(s) for array unwrapping
+    /**********************************************************
+     */
+    
+    public void testCalendarArrayUnwrap() throws Exception
+    {
+        ObjectReader reader = new ObjectMapper()
+                .readerFor(CalendarBean.class)
+                .without(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
+        final String inputDate = "1972-12-28T00:00:00.000+0000";
+        final String input = aposToQuotes("{'v':['"+inputDate+"']}");
+        try {
+            reader.readValue(input);
+            fail("Did not throw exception when reading a value from a single value array with the UNWRAP_SINGLE_VALUE_ARRAYS feature disabled");
+        } catch (MismatchedInputException exp) {
+            verifyException(exp, "Cannot deserialize");
+            verifyException(exp, "out of START_ARRAY");
+        }
+
+        reader = reader.with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
+        CalendarBean bean = reader.readValue(input);
+        assertNotNull(bean._v);
+        assertEquals(1972, bean._v.get(Calendar.YEAR));
+
+        // and finally, a fail due to multiple values:
+        try {
+            reader.readValue(aposToQuotes("{'v':['"+inputDate+"','"+inputDate+"']}"));
+            fail("Did not throw exception while reading a value from a multi value array with UNWRAP_SINGLE_VALUE_ARRAY feature enabled");
+        } catch (JsonMappingException exp) {
+            verifyException(exp, "Attempted to unwrap");
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Tests for leniency
+    /**********************************************************
+     */
+
+    public void testLenientCalendar() throws Exception
+    {
+        final String JSON = aposToQuotes("{'value':'2015-11-32'}");
+
+        // with lenient, can parse fine
+        LenientCalendarBean lenBean = MAPPER.readValue(JSON, LenientCalendarBean.class);
+        assertEquals(Calendar.DECEMBER, lenBean.value.get(Calendar.MONTH));
+        assertEquals(2, lenBean.value.get(Calendar.DAY_OF_MONTH));
+
+        // with strict, ought to produce exception
+        try {
+            MAPPER.readValue(JSON, StrictCalendarBean.class);
+            fail("Should not pass with invalid (with strict) date value");
+        } catch (MismatchedInputException e) {
+            verifyException(e, "Cannot deserialize value of type `java.util.Calendar`");
+            verifyException(e, "from String \"2015-11-32\"");
+            verifyException(e, "expected format");
+        }
+
+        // similarly with Date...
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.configOverride(java.util.Date.class)
+            .setFormat(JsonFormat.Value.forLeniency(Boolean.FALSE));
+        try {
+            mapper.readValue(quote("2015-11-32"), java.util.Date.class);
+            fail("Should not pass with invalid (with strict) date value");
+        } catch (MismatchedInputException e) {
+            verifyException(e, "Cannot deserialize value of type `java.util.Date`");
+            verifyException(e, "from String \"2015-11-32\"");
+            verifyException(e, "expected format");
+        }
     }
 
     /*
@@ -538,7 +770,7 @@
             MAPPER.readValue(quote("foobar"), Date.class);
             fail("Should have failed with an exception");
         } catch (InvalidFormatException e) {
-            verifyException(e, "Can not deserialize value of type java.util.Date from String");
+            verifyException(e, "Cannot deserialize value of type `java.util.Date` from String");
             assertEquals("foobar", e.getValue());
             assertEquals(Date.class, e.getTargetType());
         } catch (Exception e) {
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/EnumAltIdTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/EnumAltIdTest.java
new file mode 100644
index 0000000..76f1882
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/EnumAltIdTest.java
@@ -0,0 +1,132 @@
+package com.fasterxml.jackson.databind.deser.jdk;
+
+import java.io.IOException;
+import java.util.EnumSet;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.MapperFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectReader;
+import com.fasterxml.jackson.databind.exc.InvalidFormatException;
+
+public class EnumAltIdTest extends BaseMapTest
+{
+    // [databind#1313]
+
+    enum TestEnum { JACKSON, RULES, OK; }
+    protected enum LowerCaseEnum {
+        A, B, C;
+        private LowerCaseEnum() { }
+        @Override
+        public String toString() { return name().toLowerCase(); }
+    }
+
+    protected static class EnumBean {
+        @JsonFormat(with={ JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES })
+        public TestEnum value;
+    }
+
+    protected static class StrictCaseBean {
+        @JsonFormat(without={ JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES })
+        public TestEnum value;
+    }
+    
+    /*
+    /**********************************************************
+    /* Test methods, basic
+    /**********************************************************
+     */
+
+    protected final ObjectMapper MAPPER = new ObjectMapper();
+    protected final ObjectMapper MAPPER_IGNORE_CASE;
+    {
+        MAPPER_IGNORE_CASE = new ObjectMapper();
+        MAPPER_IGNORE_CASE.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS);
+    }
+
+    protected final ObjectReader READER_DEFAULT = MAPPER.reader();
+    protected final ObjectReader READER_IGNORE_CASE = MAPPER_IGNORE_CASE.reader();
+
+    // Tests for [databind#1313], case-insensitive
+
+    public void testFailWhenCaseSensitiveAndNameIsNotUpperCase() throws IOException {
+        try {
+            READER_DEFAULT.forType(TestEnum.class).readValue("\"Jackson\"");
+            fail("InvalidFormatException expected");
+        } catch (InvalidFormatException e) {
+            verifyException(e, "value not one of declared Enum instance names: [JACKSON, OK, RULES]");
+        }
+    }
+    
+    public void testFailWhenCaseSensitiveAndToStringIsUpperCase() throws IOException {
+        ObjectReader r = READER_DEFAULT.forType(LowerCaseEnum.class)
+                .with(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
+        try {
+            r.readValue("\"A\"");
+            fail("InvalidFormatException expected");
+        } catch (InvalidFormatException e) {
+            verifyException(e, "value not one of declared Enum instance names: [a, b, c]");
+        }
+    }
+
+    public void testEnumDesIgnoringCaseWithLowerCaseContent() throws IOException {
+        assertEquals(TestEnum.JACKSON,
+                READER_IGNORE_CASE.forType(TestEnum.class).readValue(quote("jackson")));
+    }
+
+    public void testEnumDesIgnoringCaseWithUpperCaseToString() throws IOException {
+        ObjectReader r = MAPPER_IGNORE_CASE.readerFor(LowerCaseEnum.class)
+                .with(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
+        assertEquals(LowerCaseEnum.A, r.readValue("\"A\""));
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods, containers
+    /**********************************************************
+     */
+
+    public void testIgnoreCaseInEnumList() throws Exception {
+        TestEnum[] enums = READER_IGNORE_CASE.forType(TestEnum[].class)
+            .readValue("[\"jacksON\", \"ruLes\"]");
+
+        assertEquals(2, enums.length);
+        assertEquals(TestEnum.JACKSON, enums[0]);
+        assertEquals(TestEnum.RULES, enums[1]);
+    }
+
+    public void testIgnoreCaseInEnumSet() throws IOException {
+        ObjectReader r = READER_IGNORE_CASE.forType(new TypeReference<EnumSet<TestEnum>>() { });
+        EnumSet<TestEnum> set = r.readValue("[\"jackson\"]");
+        assertEquals(1, set.size());
+        assertTrue(set.contains(TestEnum.JACKSON));
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods, property overrides
+    /**********************************************************
+     */
+
+    public void testIgnoreCaseViaFormat() throws Exception
+    {
+        final String JSON = aposToQuotes("{'value':'ok'}");
+
+        // should be able to allow on per-case basis:
+        EnumBean pojo = READER_DEFAULT.forType(EnumBean.class)
+            .readValue(JSON);
+        assertEquals(TestEnum.OK, pojo.value);
+
+        // including disabling acceptance
+        try {
+            READER_DEFAULT.forType(StrictCaseBean.class)
+                    .readValue(JSON);
+            fail("Should not pass");
+        } catch (InvalidFormatException e) {
+            verifyException(e, "value not one of declared Enum instance names: [JACKSON, OK, RULES]");
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/EnumDefaultReadTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/EnumDefaultReadTest.java
similarity index 97%
rename from src/test/java/com/fasterxml/jackson/databind/deser/EnumDefaultReadTest.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/jdk/EnumDefaultReadTest.java
index f1ef94f..58392e8 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/EnumDefaultReadTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/EnumDefaultReadTest.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.deser;
+package com.fasterxml.jackson.databind.deser.jdk;
 
 import java.io.IOException;
 
@@ -13,7 +13,7 @@
         ZERO,
         ONE;
     }
-    
+
     enum SimpleEnumWithDefault {
         @JsonEnumDefaultValue
         ZERO,
@@ -165,8 +165,8 @@
         throws Exception
     {
         ObjectReader r = MAPPER.reader()
-                          .with(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS)
-                          .with(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE);
+                .with(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS)
+                .with(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE);
 
         _verifyOkDeserialization(r, "ZERO", SimpleEnum.class, SimpleEnum.ZERO);
         _verifyOkDeserialization(r, "ONE", SimpleEnum.class, SimpleEnum.ONE);
@@ -222,7 +222,7 @@
             reader.forType(toValueType).readValue(quote(fromValue));
             fail("Deserialization should have failed");
         } catch (InvalidFormatException e) {
-            verifyException(e, "Can not deserialize value of type");
+            verifyException(e, "Cannot deserialize value of type");
             /* Expected. */
         }
     }
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/EnumDeserializationTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/EnumDeserializationTest.java
similarity index 86%
rename from src/test/java/com/fasterxml/jackson/databind/deser/EnumDeserializationTest.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/jdk/EnumDeserializationTest.java
index a1b6782..ff0a29c 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/EnumDeserializationTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/EnumDeserializationTest.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.deser;
+package com.fasterxml.jackson.databind.deser.jdk;
 
 import java.io.IOException;
 import java.util.*;
@@ -11,6 +11,8 @@
 import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
 import com.fasterxml.jackson.databind.deser.std.FromStringDeserializer;
 import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import com.fasterxml.jackson.databind.exc.InvalidFormatException;
+import com.fasterxml.jackson.databind.exc.MismatchedInputException;
 import com.fasterxml.jackson.databind.module.SimpleModule;
 
 @SuppressWarnings("serial")
@@ -46,7 +48,7 @@
             return TestEnum.valueOf(jp.getText().toUpperCase());
         }
     }
-    
+
     protected enum LowerCaseEnum {
         A, B, C;
         private LowerCaseEnum() { }
@@ -116,6 +118,17 @@
         }
     }
 
+    static enum StrictEnumCreator {
+        A, B;
+
+        @JsonCreator public static StrictEnumCreator fromId(String value) {
+            for (StrictEnumCreator e: values()) {
+                if (e.name().toLowerCase().equals(value)) return e;
+            }
+            throw new IllegalArgumentException(value);
+        }
+    }
+
     // // 
     
     public enum AnEnum {
@@ -170,17 +183,6 @@
         }
     }
 
-    // [databind#1626]
-    enum NumberEnum {
-        @JsonProperty("2")
-        EN2,
-        @JsonProperty("0")
-        EN0,
-        @JsonProperty("1")
-        EN1
-        ;
-    }
-
     /*
     /**********************************************************
     /* Test methods
@@ -199,9 +201,7 @@
         assertEquals(TestEnum.OK, MAPPER.readValue(jp, TestEnum.class));
         assertEquals(TestEnum.RULES, MAPPER.readValue(jp, TestEnum.class));
 
-        /* should be ok; nulls are typeless; handled by mapper, not by
-         * deserializer
-         */
+        // should be ok; nulls are typeless; handled by mapper, not by deserializer
         assertNull(MAPPER.readValue(jp, TestEnum.class));
 
         // and no more content beyond that...
@@ -214,7 +214,7 @@
         try {
             /*Object result =*/ MAPPER.readValue("\"NO-SUCH-VALUE\"", TestEnum.class);
             fail("Expected an exception for bogus enum value...");
-        } catch (JsonMappingException jex) {
+        } catch (MismatchedInputException jex) {
             verifyException(jex, "value not one of declared");
         }
         jp.close();
@@ -244,13 +244,6 @@
         assertEquals(AnnotatedTestEnum.OK, e);
     }
 
-    public void testEnumMaps() throws Exception
-    {
-        EnumMap<TestEnum,String> value = MAPPER.readValue("{\"OK\":\"value\"}",
-                new TypeReference<EnumMap<TestEnum,String>>() { });
-        assertEquals("value", value.get(TestEnum.OK));
-    }
-
     public void testSubclassedEnums() throws Exception
     {
         EnumWithSubClass value = MAPPER.readValue("\"A\"", EnumWithSubClass.class);
@@ -266,16 +259,6 @@
         assertEquals(LowerCaseEnum.C, value);
     }
 
-    public void testToStringEnumMaps() throws Exception
-    {
-        // can't reuse global one due to reconfig
-        ObjectMapper m = new ObjectMapper();
-        m.configure(DeserializationFeature.READ_ENUMS_USING_TO_STRING, true);
-        EnumMap<LowerCaseEnum,String> value = m.readValue("{\"a\":\"value\"}",
-                new TypeReference<EnumMap<LowerCaseEnum,String>>() { });
-        assertEquals("value", value.get(LowerCaseEnum.A));
-    }
-
     public void testNumbersToEnums() throws Exception
     {
         // by default numbers are fine:
@@ -289,8 +272,8 @@
         try {
             value = r.readValue("1");
             fail("Expected an error");
-        } catch (JsonMappingException e) {
-            verifyException(e, "Can not deserialize");
+        } catch (MismatchedInputException e) {
+            verifyException(e, "Cannot deserialize");
             verifyException(e, "not allowed to deserialize Enum value out of number: disable");
         }
 
@@ -298,8 +281,8 @@
         try {
             value = r.readValue(quote("1"));
             fail("Expected an error");
-        } catch (JsonMappingException e) {
-            verifyException(e, "Can not deserialize");
+        } catch (MismatchedInputException e) {
+            verifyException(e, "Cannot deserialize");
             // 26-Jan-2017, tatu: as per [databind#1505], should fail bit differently
             verifyException(e, "value not one of declared Enum");
         }
@@ -343,12 +326,23 @@
 
     public void testAllowUnknownEnumValuesReadAsNull() throws Exception
     {
-        // can not use shared mapper when changing configs...
+        // cannot use shared mapper when changing configs...
         ObjectReader reader = MAPPER.reader(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL);
         assertNull(reader.forType(TestEnum.class).readValue("\"NO-SUCH-VALUE\""));
         assertNull(reader.forType(TestEnum.class).readValue(" 4343 "));
     }
 
+    // Ability to ignore unknown Enum values:
+
+    // [databind#1642]
+    public void testAllowUnknownEnumValuesReadAsNullWithCreatorMethod() throws Exception
+    {
+        // cannot use shared mapper when changing configs...
+        ObjectReader reader = MAPPER.reader(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL);
+        assertNull(reader.forType(StrictEnumCreator.class).readValue("\"NO-SUCH-VALUE\""));
+        assertNull(reader.forType(StrictEnumCreator.class).readValue(" 4343 "));
+    }
+
     public void testAllowUnknownEnumValuesForEnumSets() throws Exception
     {
         ObjectReader reader = MAPPER.reader(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL);
@@ -371,8 +365,8 @@
          try {
              MAPPER.readValue("{\"map\":{\"NO-SUCH-VALUE\":\"val\"}}", ClassWithEnumMapKey.class);
              fail("Expected an exception for bogus enum value...");
-         } catch (JsonMappingException jex) {
-             verifyException(jex, "Can not deserialize Map key of type com.fasterxml.jackson.databind.deser");
+         } catch (InvalidFormatException jex) {
+             verifyException(jex, "Cannot deserialize Map key of type `com.fasterxml.jackson.databind.deser.jdk.EnumDeserializationTest$TestEnum`");
          }
     }
 
@@ -397,22 +391,22 @@
 
     // [databind#381]
     public void testUnwrappedEnum() throws Exception {
-        final ObjectMapper mapper = new ObjectMapper();
+        final ObjectMapper mapper = newObjectMapper();
         mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
         
         assertEquals(TestEnum.JACKSON, mapper.readValue("[" + quote("JACKSON") + "]", TestEnum.class));
     }
     
     public void testUnwrappedEnumException() throws Exception {
-        final ObjectMapper mapper = new ObjectMapper();
+        final ObjectMapper mapper = newObjectMapper();
         mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
         try {
             Object v = mapper.readValue("[" + quote("JACKSON") + "]",
                     TestEnum.class);
             fail("Exception was not thrown on deserializing a single array element of type enum; instead got: "+v);
-        } catch (JsonMappingException exp) {
+        } catch (MismatchedInputException exp) {
             //exception as thrown correctly
-            verifyException(exp, "Can not deserialize");
+            verifyException(exp, "Cannot deserialize");
         }
     }
 
@@ -426,6 +420,18 @@
         // but also with quoted Strings
         en = MAPPER.readValue(quote("1"), TestEnum.class);
         assertSame(TestEnum.values()[1], en);
+
+        // [databind#1690]: unless prevented
+        final ObjectMapper mapper = newObjectMapper();
+        mapper.disable(MapperFeature.ALLOW_COERCION_OF_SCALARS);
+        try {
+            en = mapper.readValue(quote("1"), TestEnum.class);
+            fail("Should not pass");
+        } catch (MismatchedInputException e) {
+            verifyException(e, "Cannot deserialize value of type");
+            verifyException(e, "EnumDeserializationTest$TestEnum");
+            verifyException(e, "value looks like quoted Enum index");
+        }
     }
 
     public void testEnumWithJsonPropertyRename() throws Exception
@@ -510,23 +516,15 @@
         assertNull("When using a constructor, the default value annotation shouldn't be used.", myEnum);
     }
 
-    public void testExceptionFromCustomEnumKeyDeserializer() {
-        ObjectMapper objectMapper = new ObjectMapper();
-        objectMapper.registerModule(new EnumModule());
+    public void testExceptionFromCustomEnumKeyDeserializer() throws Exception {
+        ObjectMapper mapper = newObjectMapper()
+                .registerModule(new EnumModule());
         try {
-            objectMapper.readValue("{\"TWO\": \"dumpling\"}",
+            mapper.readValue("{\"TWO\": \"dumpling\"}",
                     new TypeReference<Map<AnEnum, String>>() {});
             fail("No exception");
-        } catch (IOException e) {
+        } catch (MismatchedInputException e) {
             assertTrue(e.getMessage().contains("Undefined AnEnum"));
         }
     }
-
-    // [databind#1626]
-    public void testNumericEnumName() throws Exception
-    {
-        String json = MAPPER.writeValueAsString(NumberEnum.EN2);
-        assertEquals(quote("2"), json);
-        assertEquals(NumberEnum.EN2, MAPPER.readValue(json, NumberEnum.class));
-    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/EnumMapDeserializationTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/EnumMapDeserializationTest.java
new file mode 100644
index 0000000..bf30d14
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/EnumMapDeserializationTest.java
@@ -0,0 +1,223 @@
+package com.fasterxml.jackson.databind.deser.jdk;
+
+import java.util.EnumMap;
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.*;
+
+@SuppressWarnings("serial")
+public class EnumMapDeserializationTest extends BaseMapTest
+{
+    enum TestEnum { JACKSON, RULES, OK; }
+
+    enum TestEnumWithDefault {
+        JACKSON, RULES,
+        @JsonEnumDefaultValue
+        OK; 
+    }
+    
+    protected enum LowerCaseEnum {
+        A, B, C;
+        private LowerCaseEnum() { }
+        @Override
+        public String toString() { return name().toLowerCase(); }
+    }
+
+    static class MySimpleEnumMap extends EnumMap<TestEnum,String> { 
+        public MySimpleEnumMap() {
+            super(TestEnum.class);
+        }
+    }
+
+    static class FromStringEnumMap extends EnumMap<TestEnum,String> { 
+        @JsonCreator
+        public FromStringEnumMap(String value) {
+            super(TestEnum.class);
+            put(TestEnum.JACKSON, value);
+        }
+    }
+
+    static class FromDelegateEnumMap extends EnumMap<TestEnum,String> { 
+        @JsonCreator
+        public FromDelegateEnumMap(Map<Object,Object> stuff) {
+            super(TestEnum.class);
+            put(TestEnum.OK, String.valueOf(stuff));
+        }
+    }
+
+    static class FromPropertiesEnumMap extends EnumMap<TestEnum,String> { 
+        int a0, b0;
+
+        @JsonCreator
+        public FromPropertiesEnumMap(@JsonProperty("a") int a,
+                @JsonProperty("b") int b) {
+            super(TestEnum.class);
+            a0 = a;
+            b0 = b;
+        }
+    }
+
+    // [databind#1859]
+    public enum Enum1859 {
+        A, B, C;
+    }
+
+    static class Pojo1859
+    {
+        public EnumMap<Enum1859, String> values;
+
+        public Pojo1859() { }
+        public Pojo1859(EnumMap<Enum1859, String> v) {
+            values = v;
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods, basic
+    /**********************************************************
+     */
+
+    protected final ObjectMapper MAPPER = new ObjectMapper();
+
+    public void testEnumMaps() throws Exception
+    {
+        EnumMap<TestEnum,String> value = MAPPER.readValue("{\"OK\":\"value\"}",
+                new TypeReference<EnumMap<TestEnum,String>>() { });
+        assertEquals("value", value.get(TestEnum.OK));
+    }
+
+    public void testToStringEnumMaps() throws Exception
+    {
+        // can't reuse global one due to reconfig
+        ObjectReader r = MAPPER.reader()
+                .with(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
+        EnumMap<LowerCaseEnum,String> value = r.forType(
+            new TypeReference<EnumMap<LowerCaseEnum,String>>() { })
+                .readValue("{\"a\":\"value\"}");
+        assertEquals("value", value.get(LowerCaseEnum.A));
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods: custom enum maps
+    /**********************************************************
+     */
+
+    public void testCustomEnumMapWithDefaultCtor() throws Exception
+    {
+        MySimpleEnumMap map = MAPPER.readValue(aposToQuotes("{'RULES':'waves'}"),
+                MySimpleEnumMap.class);   
+        assertEquals(1, map.size());
+        assertEquals("waves", map.get(TestEnum.RULES));
+    }
+
+    public void testCustomEnumMapFromString() throws Exception
+    {
+        FromStringEnumMap map = MAPPER.readValue(quote("kewl"), FromStringEnumMap.class);   
+        assertEquals(1, map.size());
+        assertEquals("kewl", map.get(TestEnum.JACKSON));
+    }
+
+    public void testCustomEnumMapWithDelegate() throws Exception
+    {
+        FromDelegateEnumMap map = MAPPER.readValue(aposToQuotes("{'foo':'bar'}"), FromDelegateEnumMap.class);   
+        assertEquals(1, map.size());
+        assertEquals("{foo=bar}", map.get(TestEnum.OK));
+    }
+
+    public void testCustomEnumMapFromProps() throws Exception
+    {
+        FromPropertiesEnumMap map = MAPPER.readValue(aposToQuotes(
+                "{'a':13,'RULES':'jackson','b':-731,'OK':'yes'}"),
+                FromPropertiesEnumMap.class);
+
+        assertEquals(13, map.a0);
+        assertEquals(-731, map.b0);
+
+        assertEquals("jackson", map.get(TestEnum.RULES));
+        assertEquals("yes", map.get(TestEnum.OK));
+        assertEquals(2, map.size());
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods: polymorphic
+    /**********************************************************
+     */
+
+    // [databind#1859]
+    public void testEnumMapAsPolymorphic() throws Exception
+    {
+        EnumMap<Enum1859, String> enumMap = new EnumMap<>(Enum1859.class);
+        enumMap.put(Enum1859.A, "Test");
+        enumMap.put(Enum1859.B, "stuff");
+        Pojo1859 input = new Pojo1859(enumMap);
+
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.enableDefaultTypingAsProperty(ObjectMapper.DefaultTyping.NON_FINAL, "@type");
+
+        // 05-Mar-2018, tatu: Original issue had this; should not make difference:
+         /*
+        TypeResolverBuilder<?> mapTyperAsPropertyType = new ObjectMapper.DefaultTypeResolverBuilder(ObjectMapper.DefaultTyping.NON_FINAL);
+        mapTyperAsPropertyType.init(JsonTypeInfo.Id.CLASS, null);
+        mapTyperAsPropertyType.inclusion(JsonTypeInfo.As.PROPERTY);
+        mapper.setDefaultTyping(mapTyperAsPropertyType);
+         */
+
+        String json = mapper.writeValueAsString(input);
+        Pojo1859 result = mapper.readValue(json, Pojo1859.class);
+        assertNotNull(result);
+        assertNotNull(result.values);
+        assertEquals(2, result.values.size());
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods: handling of invalid values
+    /**********************************************************
+     */
+
+    // [databind#1859]
+    public void testUnknownKeyAsDefault() throws Exception
+    {
+        // first, via EnumMap
+        EnumMap<TestEnumWithDefault,String> value = MAPPER
+                .readerFor(new TypeReference<EnumMap<TestEnumWithDefault,String>>() { })
+                .with(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE)
+                .readValue("{\"unknown\":\"value\"}");
+        assertEquals(1, value.size());
+        assertEquals("value", value.get(TestEnumWithDefault.OK));
+
+        Map<TestEnumWithDefault,String> value2 = MAPPER
+                .readerFor(new TypeReference<Map<TestEnumWithDefault,String>>() { })
+                .with(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE)
+                .readValue("{\"unknown\":\"value\"}");
+        assertEquals(1, value2.size());
+        assertEquals("value", value2.get(TestEnumWithDefault.OK));
+    }
+
+    // [databind#1859]
+    public void testUnknownKeyAsNull() throws Exception
+    {
+        // first, via EnumMap
+        EnumMap<TestEnumWithDefault,String> value = MAPPER
+                .readerFor(new TypeReference<EnumMap<TestEnumWithDefault,String>>() { })
+                .with(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)
+                .readValue("{\"unknown\":\"value\"}");
+        assertEquals(0, value.size());
+
+        // then regular Map
+        Map<TestEnumWithDefault,String> value2 = MAPPER
+                .readerFor(new TypeReference<Map<TestEnumWithDefault,String>>() { })
+                .with(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)
+                .readValue("{\"unknown\":\"value\"}");
+        // 04-Jan-2017, tatu: Not sure if this is weird or not, but since `null`s are typically
+        //    ok for "regular" JDK Maps...
+        assertEquals(1, value2.size());
+        assertEquals("value", value2.get(null));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/JDKAtomicTypesTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKAtomicTypesTest.java
similarity index 95%
rename from src/test/java/com/fasterxml/jackson/databind/deser/JDKAtomicTypesTest.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKAtomicTypesTest.java
index a3236cf..5721556 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/JDKAtomicTypesTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKAtomicTypesTest.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.deser;
+package com.fasterxml.jackson.databind.deser.jdk;
 
 import java.io.Serializable;
 import java.math.BigDecimal;
@@ -40,7 +40,6 @@
     static class SimpleWrapper {
         public AtomicReference<Object> value;
 
-        public SimpleWrapper() { }
         public SimpleWrapper(Object o) { value = new AtomicReference<Object>(o); }
     }
 
@@ -150,7 +149,7 @@
         JsonInclude.Value incl =
                 JsonInclude.Value.construct(JsonInclude.Include.NON_ABSENT, JsonInclude.Include.ALWAYS);
         ObjectMapper mapper = new ObjectMapper();
-        mapper.setPropertyInclusion(incl);
+        mapper.setDefaultPropertyInclusion(incl);
         assertEquals(aposToQuotes("{'value':true}"),
                 mapper.writeValueAsString(new SimpleWrapper(Boolean.TRUE)));
     }
@@ -160,7 +159,7 @@
         JsonInclude.Value incl =
                 JsonInclude.Value.construct(JsonInclude.Include.NON_ABSENT, JsonInclude.Include.NON_NULL);
         ObjectMapper mapper = new ObjectMapper();
-        mapper.setPropertyInclusion(incl);
+        mapper.setDefaultPropertyInclusion(incl);
         assertEquals(aposToQuotes("{'value':true}"),
                 mapper.writeValueAsString(new SimpleWrapper(Boolean.TRUE)));
     }
@@ -170,7 +169,7 @@
         JsonInclude.Value incl =
                 JsonInclude.Value.construct(JsonInclude.Include.NON_ABSENT, JsonInclude.Include.NON_ABSENT);
         ObjectMapper mapper = new ObjectMapper();
-        mapper.setPropertyInclusion(incl);
+        mapper.setDefaultPropertyInclusion(incl);
         assertEquals(aposToQuotes("{'value':true}"),
                 mapper.writeValueAsString(new SimpleWrapper(Boolean.TRUE)));
     }
@@ -180,7 +179,7 @@
         JsonInclude.Value incl =
                 JsonInclude.Value.construct(JsonInclude.Include.NON_ABSENT, JsonInclude.Include.NON_EMPTY);
         ObjectMapper mapper = new ObjectMapper();
-        mapper.setPropertyInclusion(incl);
+        mapper.setDefaultPropertyInclusion(incl);
         assertEquals(aposToQuotes("{'value':true}"),
                 mapper.writeValueAsString(new SimpleWrapper(Boolean.TRUE)));
     }
@@ -205,12 +204,12 @@
         ObjectMapper mapper = MAPPER;
 
         // by default, include as null
-        assertEquals("{\"value\":null}", mapper.writeValueAsString(input));
+        assertEquals(aposToQuotes("{'value':null}"), mapper.writeValueAsString(input));
 
         // ditto with "no nulls"
         mapper = new ObjectMapper().setSerializationInclusion(JsonInclude
                 .Include.NON_NULL);
-        assertEquals("{\"value\":null}", mapper.writeValueAsString(input));
+        assertEquals(aposToQuotes("{'value':null}"), mapper.writeValueAsString(input));
 
         // but not with "non empty"
         mapper = new ObjectMapper().setSerializationInclusion(JsonInclude
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKCollectionsDeserTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKCollectionsDeserTest.java
new file mode 100644
index 0000000..875c61c
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKCollectionsDeserTest.java
@@ -0,0 +1,71 @@
+package com.fasterxml.jackson.databind.deser.jdk;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Tests for special collection/map types via `java.util.Collections`
+ */
+public class JDKCollectionsDeserTest extends BaseMapTest
+{
+    static class XBean {
+        public int x;
+
+        public XBean() { }
+        public XBean(int x) { this.x = x; }
+    }
+
+    /*
+    /**********************************************************************
+    /* Test methods
+    /**********************************************************************
+     */
+
+    private final static ObjectMapper MAPPER = new ObjectMapper();
+    
+    // And then a round-trip test for singleton collections
+    public void testSingletonCollections() throws Exception
+    {
+        final TypeReference<?> xbeanListType = new TypeReference<List<XBean>>() { };
+
+        String json = MAPPER.writeValueAsString(Collections.singleton(new XBean(3)));
+        Collection<XBean> result = MAPPER.readValue(json, xbeanListType);
+        assertNotNull(result);
+        assertEquals(1, result.size());
+        assertEquals(3, result.iterator().next().x);
+
+        json = MAPPER.writeValueAsString(Collections.singletonList(new XBean(28)));
+        result = MAPPER.readValue(json, xbeanListType);
+        assertNotNull(result);
+        assertEquals(1, result.size());
+        assertEquals(28, result.iterator().next().x);
+    }
+
+    // [databind#1868]: Verify class name serialized as is
+    public void testUnmodifiableSet() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
+
+        Set<String> theSet = Collections.unmodifiableSet(Collections.singleton("a"));
+        String json = mapper.writeValueAsString(theSet);
+
+        assertEquals("[\"java.util.Collections$UnmodifiableSet\",[\"a\"]]", json);
+
+        // 04-Jan-2018, tatu: Alas, no way to make this actually work well, at this point.
+         //   In theory could jiggle things back on deser, using one of two ways:
+         //
+         //   1) Do mapping to regular Set/List types (abstract type mapping): would work, but get rid of immutability
+         //   2) Have actually separate deserializer OR ValueInstantiator
+        /*
+        Set<String> result = mapper.readValue(json, Set.class);
+        assertNotNull(result);
+        assertEquals(1, result.size());
+        */
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/JDKNumberDeserTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKNumberDeserTest.java
similarity index 77%
rename from src/test/java/com/fasterxml/jackson/databind/deser/JDKNumberDeserTest.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKNumberDeserTest.java
index c224f88..2fc0b1c 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/JDKNumberDeserTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKNumberDeserTest.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.deser;
+package com.fasterxml.jackson.databind.deser.jdk;
 
 import java.io.IOException;
 import java.io.StringReader;
@@ -10,6 +10,7 @@
 import com.fasterxml.jackson.core.*;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.exc.MismatchedInputException;
 
 public class JDKNumberDeserTest extends BaseMapTest
 {
@@ -81,16 +82,65 @@
         assertEquals(Double.valueOf(Double.NEGATIVE_INFINITY), result);
     }
 
+    // 01-Mar-2017, tatu: This is bit tricky... in some ways, mapping to "empty value"
+    //    would be best; but due to legacy reasons becomes `null` at this point
     public void testEmptyAsNumber() throws Exception
     {
+        assertNull(MAPPER.readValue(quote(""), Byte.class));
+        assertNull(MAPPER.readValue(quote(""), Short.class));
+        assertNull(MAPPER.readValue(quote(""), Character.class));
         assertNull(MAPPER.readValue(quote(""), Integer.class));
         assertNull(MAPPER.readValue(quote(""), Long.class));
         assertNull(MAPPER.readValue(quote(""), Float.class));
         assertNull(MAPPER.readValue(quote(""), Double.class));
+
         assertNull(MAPPER.readValue(quote(""), BigInteger.class));
         assertNull(MAPPER.readValue(quote(""), BigDecimal.class));
     }
 
+    public void testTextualNullAsNumber() throws Exception
+    {
+        final String NULL_JSON = quote("null");
+        assertNull(MAPPER.readValue(NULL_JSON, Byte.class));
+        assertNull(MAPPER.readValue(NULL_JSON, Short.class));
+        // Character is bit special, can't do:
+//        assertNull(MAPPER.readValue(JSON, Character.class));
+        assertNull(MAPPER.readValue(NULL_JSON, Integer.class));
+        assertNull(MAPPER.readValue(NULL_JSON, Long.class));
+        assertNull(MAPPER.readValue(NULL_JSON, Float.class));
+        assertNull(MAPPER.readValue(NULL_JSON, Double.class));
+
+        assertEquals(Byte.valueOf((byte) 0), MAPPER.readValue(NULL_JSON, Byte.TYPE));
+        assertEquals(Short.valueOf((short) 0), MAPPER.readValue(NULL_JSON, Short.TYPE));
+        // Character is bit special, can't do:
+//        assertEquals(Character.valueOf((char) 0), MAPPER.readValue(JSON, Character.TYPE));
+        assertEquals(Integer.valueOf(0), MAPPER.readValue(NULL_JSON, Integer.TYPE));
+        assertEquals(Long.valueOf(0L), MAPPER.readValue(NULL_JSON, Long.TYPE));
+        assertEquals(Float.valueOf(0f), MAPPER.readValue(NULL_JSON, Float.TYPE));
+        assertEquals(Double.valueOf(0d), MAPPER.readValue(NULL_JSON, Double.TYPE));
+        
+        assertNull(MAPPER.readValue(NULL_JSON, BigInteger.class));
+        assertNull(MAPPER.readValue(NULL_JSON, BigDecimal.class));
+
+        // Also: verify failure for at least some
+        try {
+            MAPPER.readerFor(Integer.TYPE).with(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)
+                .readValue(NULL_JSON);
+            fail("Should not have passed");
+        } catch (MismatchedInputException e) {
+            verifyException(e, "Cannot coerce String \"null\"");
+        }
+
+        ObjectMapper noCoerceMapper = new ObjectMapper();
+        noCoerceMapper.disable(MapperFeature.ALLOW_COERCION_OF_SCALARS);
+        try {
+            noCoerceMapper.readValue(NULL_JSON, Integer.TYPE);
+            fail("Should not have passed");
+        } catch (MismatchedInputException e) {
+            verifyException(e, "Cannot coerce String \"null\"");
+        }
+    }
+    
     public void testDeserializeDecimalHappyPath() throws Exception {
         String json = "{\"defaultValue\": { \"value\": 123 } }";
         MyBeanHolder result = MAPPER.readValue(json, MyBeanHolder.class);
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKScalarsTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKScalarsTest.java
new file mode 100644
index 0000000..66d9951
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKScalarsTest.java
@@ -0,0 +1,849 @@
+package com.fasterxml.jackson.databind.deser.jdk;
+
+import java.io.*;
+import java.lang.reflect.Array;
+
+import org.junit.Assert;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Unit tests for verifying handling of simple basic non-structured
+ * types; primitives (and/or their wrappers), Strings.
+ */
+public class JDKScalarsTest
+    extends BaseMapTest
+{
+    final static String NAN_STRING = "NaN";
+
+    final static class BooleanBean {
+        boolean _v;
+        void setV(boolean v) { _v = v; }
+    }
+
+    static class BooleanWrapper {
+        public Boolean wrapper;
+        public boolean primitive;
+        
+        protected Boolean ctor;
+        
+        @JsonCreator
+        public BooleanWrapper(@JsonProperty("ctor") Boolean foo) {
+            ctor = foo;
+        }
+    }
+
+    static class IntBean {
+        int _v;
+        void setV(int v) { _v = v; }
+    }
+
+    static class LongBean {
+        long _v;
+        void setV(long v) { _v = v; }
+    }
+    
+    final static class DoubleBean {
+        double _v;
+        void setV(double v) { _v = v; }
+    }
+
+    final static class FloatBean {
+        float _v;
+        void setV(float v) { _v = v; }
+    }
+    
+    final static class CharacterBean {
+        char _v;
+        void setV(char v) { _v = v; }
+        char getV() { return _v; }
+    }
+    
+    final static class CharacterWrapperBean {
+        Character _v;
+        void setV(Character v) { _v = v; }
+        Character getV() { return _v; }
+    }
+
+    /**
+     * Also, let's ensure that it's ok to override methods.
+     */
+    static class IntBean2
+        extends IntBean
+    {
+        @Override
+        void setV(int v2) { super.setV(v2+1); }
+    }
+
+    static class PrimitivesBean
+    {
+        public boolean booleanValue = true;
+        public byte byteValue = 3;
+        public char charValue = 'a';
+        public short shortValue = 37;
+        public int intValue = 1;
+        public long longValue = 100L;
+        public float floatValue = 0.25f;
+        public double doubleValue = -1.0;
+    }
+
+    static class WrappersBean
+    {
+        public Boolean booleanValue;
+        public Byte byteValue;
+        public Character charValue;
+        public Short shortValue;
+        public Integer intValue;
+        public Long longValue;
+        public Float floatValue;
+        public Double doubleValue;
+    }
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+
+    /*
+    /**********************************************************
+    /* Scalar tests for boolean
+    /**********************************************************
+     */
+
+    public void testBooleanPrimitive() throws Exception
+    {
+        // first, simple case:
+        BooleanBean result = MAPPER.readValue("{\"v\":true}", BooleanBean.class);
+        assertTrue(result._v);
+        result = MAPPER.readValue("{\"v\":null}", BooleanBean.class);
+        assertNotNull(result);
+        assertFalse(result._v);
+        result = MAPPER.readValue("{\"v\":1}", BooleanBean.class);
+        assertNotNull(result);
+        assertTrue(result._v);
+
+        // should work with arrays too..
+        boolean[] array = MAPPER.readValue("[ null, false ]", boolean[].class);
+        assertNotNull(array);
+        assertEquals(2, array.length);
+        assertFalse(array[0]);
+        assertFalse(array[1]);
+    }
+
+    /**
+     * Simple unit test to verify that we can map boolean values to
+     * java.lang.Boolean.
+     */
+    public void testBooleanWrapper() throws Exception
+    {
+        Boolean result = MAPPER.readValue("true", Boolean.class);
+        assertEquals(Boolean.TRUE, result);
+        result = MAPPER.readValue("false", Boolean.class);
+        assertEquals(Boolean.FALSE, result);
+
+        // should accept ints too, (0 == false, otherwise true)
+        result = MAPPER.readValue("0", Boolean.class);
+        assertEquals(Boolean.FALSE, result);
+        result = MAPPER.readValue("1", Boolean.class);
+        assertEquals(Boolean.TRUE, result);
+    }
+
+    // Test for verifying that Long values are coerced to boolean correctly as well
+    public void testLongToBoolean() throws Exception
+    {
+        long value = 1L + Integer.MAX_VALUE;
+        BooleanWrapper b = MAPPER.readValue("{\"primitive\" : "+value+", \"wrapper\":"+value+", \"ctor\":"+value+"}",
+                BooleanWrapper.class);
+        assertEquals(Boolean.TRUE, b.wrapper);
+        assertTrue(b.primitive);
+        assertEquals(Boolean.TRUE, b.ctor);
+
+        // but ensure we can also get `false`
+        b = MAPPER.readValue("{\"primitive\" : 0 , \"wrapper\":0, \"ctor\":0}",
+                BooleanWrapper.class);
+        assertEquals(Boolean.FALSE, b.wrapper);
+        assertFalse(b.primitive);
+        assertEquals(Boolean.FALSE, b.ctor);
+
+        boolean[] boo = MAPPER.readValue("[ 0, 15, \"\", \"false\", \"True\" ]",
+                boolean[].class);
+        assertEquals(5, boo.length);
+        assertFalse(boo[0]);
+        assertTrue(boo[1]);
+        assertFalse(boo[2]);
+        assertFalse(boo[3]);
+        assertTrue(boo[4]);
+    }
+
+    /*
+    /**********************************************************
+    /* Scalar tests for integral types
+    /**********************************************************
+     */
+
+    public void testByteWrapper() throws Exception
+    {
+        Byte result = MAPPER.readValue("   -42\t", Byte.class);
+        assertEquals(Byte.valueOf((byte)-42), result);
+
+        // Also: should be able to coerce floats, strings:
+        result = MAPPER.readValue(" \"-12\"", Byte.class);
+        assertEquals(Byte.valueOf((byte)-12), result);
+
+        result = MAPPER.readValue(" 39.07", Byte.class);
+        assertEquals(Byte.valueOf((byte)39), result);
+    }
+
+    public void testShortWrapper() throws Exception
+    {
+        Short result = MAPPER.readValue("37", Short.class);
+        assertEquals(Short.valueOf((short)37), result);
+
+        // Also: should be able to coerce floats, strings:
+        result = MAPPER.readValue(" \"-1009\"", Short.class);
+        assertEquals(Short.valueOf((short)-1009), result);
+
+        result = MAPPER.readValue("-12.9", Short.class);
+        assertEquals(Short.valueOf((short)-12), result);
+    }
+
+    public void testCharacterWrapper() throws Exception
+    {
+        // First: canonical value is 1-char string
+        Character result = MAPPER.readValue("\"a\"", Character.class);
+        assertEquals(Character.valueOf('a'), result);
+
+        // But can also pass in ascii code
+        result = MAPPER.readValue(" "+((int) 'X'), Character.class);
+        assertEquals(Character.valueOf('X'), result);
+        
+        final CharacterWrapperBean wrapper = MAPPER.readValue("{\"v\":null}", CharacterWrapperBean.class);
+        assertNotNull(wrapper);
+        assertNull(wrapper.getV());
+        
+        final ObjectMapper mapper = new ObjectMapper();
+        mapper.enable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES);
+        try {
+            mapper.readValue("{\"v\":null}", CharacterBean.class);
+            fail("Attempting to deserialize a 'null' JSON reference into a 'char' property did not throw an exception");
+        } catch (JsonMappingException e) {
+            verifyException(e, "cannot map `null`");
+            //Exception thrown as required
+        }
+
+        mapper.disable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES);  
+        final CharacterBean charBean = MAPPER.readValue("{\"v\":null}", CharacterBean.class);
+        assertNotNull(wrapper);
+        assertEquals('\u0000', charBean.getV());
+    }
+
+    public void testIntWrapper() throws Exception
+    {
+        Integer result = MAPPER.readValue("   -42\t", Integer.class);
+        assertEquals(Integer.valueOf(-42), result);
+
+        // Also: should be able to coerce floats, strings:
+        result = MAPPER.readValue(" \"-1200\"", Integer.class);
+        assertEquals(Integer.valueOf(-1200), result);
+
+        result = MAPPER.readValue(" 39.07", Integer.class);
+        assertEquals(Integer.valueOf(39), result);
+    }
+
+    public void testIntPrimitive() throws Exception
+    {
+        // first, simple case:
+        IntBean result = MAPPER.readValue("{\"v\":3}", IntBean.class);
+        assertEquals(3, result._v);
+
+        result = MAPPER.readValue("{\"v\":null}", IntBean.class);
+        assertNotNull(result);
+        assertEquals(0, result._v);
+
+        // should work with arrays too..
+        int[] array = MAPPER.readValue("[ null ]", int[].class);
+        assertNotNull(array);
+        assertEquals(1, array.length);
+        assertEquals(0, array[0]);
+        
+        // [databind#381]
+        final ObjectMapper mapper = new ObjectMapper();
+        mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
+        try {
+            mapper.readValue("{\"v\":[3]}", IntBean.class);
+            fail("Did not throw exception when reading a value from a single value array with the UNWRAP_SINGLE_VALUE_ARRAYS feature disabled");
+        } catch (JsonMappingException exp) {
+            //Correctly threw exception
+        }
+        
+        mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
+        
+        result = mapper.readValue("{\"v\":[3]}", IntBean.class);
+        assertEquals(3, result._v);
+        
+        result = mapper.readValue("[{\"v\":[3]}]", IntBean.class);
+        assertEquals(3, result._v);
+        
+        try {
+            mapper.readValue("[{\"v\":[3,3]}]", IntBean.class);
+            fail("Did not throw exception while reading a value from a multi value array with UNWRAP_SINGLE_VALUE_ARRAY feature enabled");
+        } catch (JsonMappingException exp) {
+            //threw exception as required
+        }
+        
+        result = mapper.readValue("{\"v\":[null]}", IntBean.class);
+        assertNotNull(result);
+        assertEquals(0, result._v);
+
+        array = mapper.readValue("[ [ null ] ]", int[].class);
+        assertNotNull(array);
+        assertEquals(1, array.length);
+        assertEquals(0, array[0]);
+    }
+    
+    public void testLongWrapper() throws Exception
+    {
+        Long result = MAPPER.readValue("12345678901", Long.class);
+        assertEquals(Long.valueOf(12345678901L), result);
+
+        // Also: should be able to coerce floats, strings:
+        result = MAPPER.readValue(" \"-9876\"", Long.class);
+        assertEquals(Long.valueOf(-9876), result);
+
+        result = MAPPER.readValue("1918.3", Long.class);
+        assertEquals(Long.valueOf(1918), result);
+    }
+
+    public void testLongPrimitive() throws Exception
+    {
+        // first, simple case:
+        LongBean result = MAPPER.readValue("{\"v\":3}", LongBean.class);
+        assertEquals(3, result._v);
+        result = MAPPER.readValue("{\"v\":null}", LongBean.class);
+        assertNotNull(result);
+        assertEquals(0, result._v);
+
+        // should work with arrays too..
+        long[] array = MAPPER.readValue("[ null ]", long[].class);
+        assertNotNull(array);
+        assertEquals(1, array.length);
+        assertEquals(0, array[0]);
+        
+        // [databind#381]
+        final ObjectMapper mapper = new ObjectMapper();
+        mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
+        try {
+            mapper.readValue("{\"v\":[3]}", LongBean.class);
+            fail("Did not throw exception when reading a value from a single value array with the UNWRAP_SINGLE_VALUE_ARRAYS feature disabled");
+        } catch (JsonMappingException exp) {
+            //Correctly threw exception
+        }
+        
+        mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
+        
+        result = mapper.readValue("{\"v\":[3]}", LongBean.class);
+        assertEquals(3, result._v);
+        
+        result = mapper.readValue("[{\"v\":[3]}]", LongBean.class);
+        assertEquals(3, result._v);
+        
+        try {
+            mapper.readValue("[{\"v\":[3,3]}]", LongBean.class);
+            fail("Did not throw exception while reading a value from a multi value array with UNWRAP_SINGLE_VALUE_ARRAY feature enabled");
+        } catch (JsonMappingException exp) {
+            //threw exception as required
+        }
+        
+        result = mapper.readValue("{\"v\":[null]}", LongBean.class);
+        assertNotNull(result);
+        assertEquals(0, result._v);
+
+        array = mapper.readValue("[ [ null ] ]", long[].class);
+        assertNotNull(array);
+        assertEquals(1, array.length);
+        assertEquals(0, array[0]);
+    }
+
+    /**
+     * Beyond simple case, let's also ensure that method overriding works as
+     * expected.
+     */
+    public void testIntWithOverride() throws Exception
+    {
+        IntBean2 result = MAPPER.readValue("{\"v\":8}", IntBean2.class);
+        assertEquals(9, result._v);
+    }
+
+    /*
+    /**********************************************************
+    /* Scalar tests for floating point types
+    /**********************************************************
+     */
+
+    public void testDoublePrimitive() throws Exception
+    {
+        // first, simple case:
+        // bit tricky with binary fps but...
+        final double value = 0.016;
+        DoubleBean result = MAPPER.readValue("{\"v\":"+value+"}", DoubleBean.class);
+        assertEquals(value, result._v);
+        // then [JACKSON-79]:
+        result = MAPPER.readValue("{\"v\":null}", DoubleBean.class);
+        assertNotNull(result);
+        assertEquals(0.0, result._v);
+
+        // should work with arrays too..
+        double[] array = MAPPER.readValue("[ null ]", double[].class);
+        assertNotNull(array);
+        assertEquals(1, array.length);
+        assertEquals(0.0, array[0]);
+    }
+
+    /* Note: dealing with floating-point values is tricky; not sure if
+     * we can really use equality tests here... JDK does have decent
+     * conversions though, to retain accuracy and round-trippability.
+     * But still...
+     */
+    public void testFloatWrapper() throws Exception
+    {
+        // Also: should be able to coerce floats, strings:
+        String[] STRS = new String[] {
+            "1.0", "0.0", "-0.3", "0.7", "42.012", "-999.0", NAN_STRING
+        };
+
+        for (String str : STRS) {
+            Float exp = Float.valueOf(str);
+            Float result;
+
+            if (NAN_STRING != str) {
+                // First, as regular floating point value
+                result = MAPPER.readValue(str, Float.class);
+                assertEquals(exp, result);
+            }
+
+            // and then as coerced String:
+            result = MAPPER.readValue(" \""+str+"\"", Float.class);
+            assertEquals(exp, result);
+        }
+    }
+
+    public void testDoubleWrapper() throws Exception
+    {
+        // Also: should be able to coerce doubles, strings:
+        String[] STRS = new String[] {
+            "1.0", "0.0", "-0.3", "0.7", "42.012", "-999.0", NAN_STRING
+        };
+
+        for (String str : STRS) {
+            Double exp = Double.valueOf(str);
+            Double result;
+
+            // First, as regular double value
+            if (NAN_STRING != str) {
+                result = MAPPER.readValue(str, Double.class);
+               assertEquals(exp, result);
+            }
+            // and then as coerced String:
+            result = MAPPER.readValue(" \""+str+"\"", Double.class);
+            assertEquals(exp, result);
+        }
+    }
+
+    public void testDoubleAsArray() throws Exception
+    {
+        final ObjectMapper mapper = new ObjectMapper();
+        mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
+        final double value = 0.016;
+        try {
+            mapper.readValue("{\"v\":[" + value + "]}", DoubleBean.class);
+            fail("Did not throw exception when reading a value from a single value array with the UNWRAP_SINGLE_VALUE_ARRAYS feature disabled");
+        } catch (JsonMappingException exp) {
+            //Correctly threw exception
+        }
+        
+        mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
+        
+        DoubleBean result = mapper.readValue("{\"v\":[" + value + "]}",
+                DoubleBean.class);
+        assertEquals(value, result._v);
+        
+        result = mapper.readValue("[{\"v\":[" + value + "]}]", DoubleBean.class);
+        assertEquals(value, result._v);
+        
+        try {
+            mapper.readValue("[{\"v\":[" + value + "," + value + "]}]", DoubleBean.class);
+            fail("Did not throw exception while reading a value from a multi value array with UNWRAP_SINGLE_VALUE_ARRAY feature enabled");
+        } catch (JsonMappingException exp) {
+            //threw exception as required
+        }
+        
+        result = mapper.readValue("{\"v\":[null]}", DoubleBean.class);
+        assertNotNull(result);
+        assertEquals(0d, result._v);
+
+        double[] array = mapper.readValue("[ [ null ] ]", double[].class);
+        assertNotNull(array);
+        assertEquals(1, array.length);
+        assertEquals(0d, array[0]);
+    }
+
+    public void testDoublePrimitiveNonNumeric() throws Exception
+    {
+        // first, simple case:
+        // bit tricky with binary fps but...
+        double value = Double.POSITIVE_INFINITY;
+        DoubleBean result = MAPPER.readValue("{\"v\":\""+value+"\"}", DoubleBean.class);
+        assertEquals(value, result._v);
+        
+        // should work with arrays too..
+        double[] array = MAPPER.readValue("[ \"Infinity\" ]", double[].class);
+        assertNotNull(array);
+        assertEquals(1, array.length);
+        assertEquals(Double.POSITIVE_INFINITY, array[0]);
+    }
+    
+    public void testFloatPrimitiveNonNumeric() throws Exception
+    {
+        // bit tricky with binary fps but...
+        float value = Float.POSITIVE_INFINITY;
+        FloatBean result = MAPPER.readValue("{\"v\":\""+value+"\"}", FloatBean.class);
+        assertEquals(value, result._v);
+        
+        // should work with arrays too..
+        float[] array = MAPPER.readValue("[ \"Infinity\" ]", float[].class);
+        assertNotNull(array);
+        assertEquals(1, array.length);
+        assertEquals(Float.POSITIVE_INFINITY, array[0]);
+    }
+
+    /*
+    /**********************************************************
+    /* Scalar tests, other
+    /**********************************************************
+     */
+
+    public void testEmptyToNullCoercionForPrimitives() throws Exception {
+        _testEmptyToNullCoercion(int.class, Integer.valueOf(0));
+        _testEmptyToNullCoercion(long.class, Long.valueOf(0));
+        _testEmptyToNullCoercion(double.class, Double.valueOf(0.0));
+        _testEmptyToNullCoercion(float.class, Float.valueOf(0.0f));
+    }
+
+    private void _testEmptyToNullCoercion(Class<?> primType, Object emptyValue) throws Exception
+    {
+        final String EMPTY = "\"\"";
+
+        // as per [databind#1095] should only allow coercion from empty String,
+        // if `null` is acceptable
+        ObjectReader intR = MAPPER.readerFor(primType);
+        assertEquals(emptyValue, intR.readValue(EMPTY));
+        try {
+            intR.with(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)
+                .readValue("\"\"");
+            fail("Should not have passed");
+        } catch (JsonMappingException e) {
+            verifyException(e, "Cannot coerce empty String");
+        }
+    }
+
+    public void testBase64Variants() throws Exception
+    {
+        final byte[] INPUT = "abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyz1234567890X".getBytes("UTF-8");
+        
+        // default encoding is "MIME, no linefeeds", so:
+        Assert.assertArrayEquals(INPUT, MAPPER.readValue(
+                quote("YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwWA=="),
+                byte[].class));
+        ObjectReader reader = MAPPER.readerFor(byte[].class);
+        Assert.assertArrayEquals(INPUT, (byte[]) reader.with(Base64Variants.MIME_NO_LINEFEEDS).readValue(
+                quote("YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwWA=="
+        )));
+
+        // but others should be slightly different
+        Assert.assertArrayEquals(INPUT, (byte[]) reader.with(Base64Variants.MIME).readValue(
+                quote("YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwYWJjZGVmZ2hpamtsbW5vcHFyc3R1\\ndnd4eXoxMjM0NTY3ODkwWA=="
+        )));
+        Assert.assertArrayEquals(INPUT, (byte[]) reader.with(Base64Variants.MODIFIED_FOR_URL).readValue(
+                quote("YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwWA"
+        )));
+        // PEM mandates 64 char lines:
+        Assert.assertArrayEquals(INPUT, (byte[]) reader.with(Base64Variants.PEM).readValue(
+                quote("YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwYWJjZGVmZ2hpamts\\nbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwWA=="
+        )));
+    }    
+
+    /*
+    /**********************************************************
+    /* Sequence tests
+    /**********************************************************
+     */
+
+    /**
+     * Then a unit test to verify that we can conveniently bind sequence of
+     * space-separate simple values
+     */
+    public void testSequenceOfInts() throws Exception
+    {
+        final int NR_OF_INTS = 100;
+
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < NR_OF_INTS; ++i) {
+            sb.append(" ");
+            sb.append(i);
+        }
+        JsonParser jp = MAPPER.getFactory().createParser(sb.toString());
+        for (int i = 0; i < NR_OF_INTS; ++i) {
+            Integer result = MAPPER.readValue(jp, Integer.class);
+            assertEquals(Integer.valueOf(i), result);
+        }
+        jp.close();
+    }
+
+
+    /*
+    /**********************************************************
+    /* Empty String coercion, handling
+    /**********************************************************
+     */
+
+    // by default, should return nulls, n'est pas?
+    public void testEmptyStringForWrappers() throws IOException
+    {
+        WrappersBean bean;
+
+        bean = MAPPER.readValue("{\"booleanValue\":\"\"}", WrappersBean.class);
+        assertNull(bean.booleanValue);
+        bean = MAPPER.readValue("{\"byteValue\":\"\"}", WrappersBean.class);
+        assertNull(bean.byteValue);
+
+        // char/Character is different... not sure if this should work or not:
+        bean = MAPPER.readValue("{\"charValue\":\"\"}", WrappersBean.class);
+        assertNull(bean.charValue);
+
+        bean = MAPPER.readValue("{\"shortValue\":\"\"}", WrappersBean.class);
+        assertNull(bean.shortValue);
+        bean = MAPPER.readValue("{\"intValue\":\"\"}", WrappersBean.class);
+        assertNull(bean.intValue);
+        bean = MAPPER.readValue("{\"longValue\":\"\"}", WrappersBean.class);
+        assertNull(bean.longValue);
+        bean = MAPPER.readValue("{\"floatValue\":\"\"}", WrappersBean.class);
+        assertNull(bean.floatValue);
+        bean = MAPPER.readValue("{\"doubleValue\":\"\"}", WrappersBean.class);
+        assertNull(bean.doubleValue);
+    }
+
+    public void testEmptyStringForPrimitives() throws IOException
+    {
+        PrimitivesBean bean;
+        bean = MAPPER.readValue("{\"booleanValue\":\"\"}", PrimitivesBean.class);
+        assertFalse(bean.booleanValue);
+        bean = MAPPER.readValue("{\"byteValue\":\"\"}", PrimitivesBean.class);
+        assertEquals((byte) 0, bean.byteValue);
+        bean = MAPPER.readValue("{\"charValue\":\"\"}", PrimitivesBean.class);
+        assertEquals((char) 0, bean.charValue);
+        bean = MAPPER.readValue("{\"shortValue\":\"\"}", PrimitivesBean.class);
+        assertEquals((short) 0, bean.shortValue);
+        bean = MAPPER.readValue("{\"intValue\":\"\"}", PrimitivesBean.class);
+        assertEquals(0, bean.intValue);
+        bean = MAPPER.readValue("{\"longValue\":\"\"}", PrimitivesBean.class);
+        assertEquals(0L, bean.longValue);
+        bean = MAPPER.readValue("{\"floatValue\":\"\"}", PrimitivesBean.class);
+        assertEquals(0.0f, bean.floatValue);
+        bean = MAPPER.readValue("{\"doubleValue\":\"\"}", PrimitivesBean.class);
+        assertEquals(0.0, bean.doubleValue);
+    }
+
+    private void _verifyEmptyStringFailForPrimitives(String propName) throws IOException
+    {
+        final ObjectReader reader = MAPPER
+                .readerFor(PrimitivesBean.class)
+                .with(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES);
+        try {
+            reader.readValue(aposToQuotes("{'"+propName+"':''}"));
+            fail("Expected failure for boolean + empty String");
+        } catch (JsonMappingException e) {
+            verifyException(e, "Cannot coerce empty String (\"\")");
+            verifyException(e, "to Null value");
+        }
+    }
+    
+    // for [databind#403]
+    public void testEmptyStringFailForPrimitives() throws IOException
+    {
+        _verifyEmptyStringFailForPrimitives("booleanValue");
+        _verifyEmptyStringFailForPrimitives("byteValue");
+        _verifyEmptyStringFailForPrimitives("charValue");
+        _verifyEmptyStringFailForPrimitives("shortValue");
+        _verifyEmptyStringFailForPrimitives("intValue");
+        _verifyEmptyStringFailForPrimitives("longValue");
+        _verifyEmptyStringFailForPrimitives("floatValue");
+        _verifyEmptyStringFailForPrimitives("doubleValue");
+    }
+
+    /*
+    /**********************************************************
+    /* Null handling for scalars in POJO
+    /**********************************************************
+     */
+
+    public void testNullForPrimitives() throws IOException
+    {
+        // by default, ok to rely on defaults
+        PrimitivesBean bean = MAPPER.readValue(
+                "{\"intValue\":null, \"booleanValue\":null, \"doubleValue\":null}",
+                PrimitivesBean.class);
+        assertNotNull(bean);
+        assertEquals(0, bean.intValue);
+        assertEquals(false, bean.booleanValue);
+        assertEquals(0.0, bean.doubleValue);
+
+        bean = MAPPER.readValue("{\"byteValue\":null, \"longValue\":null, \"floatValue\":null}",
+                PrimitivesBean.class);
+        assertNotNull(bean);
+        assertEquals((byte) 0, bean.byteValue);
+        assertEquals(0L, bean.longValue);
+        assertEquals(0.0f, bean.floatValue);
+
+        // but not when enabled
+        final ObjectReader reader = MAPPER
+                .readerFor(PrimitivesBean.class)
+                .with(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES);
+        // boolean
+        try {
+            reader.readValue("{\"booleanValue\":null}");
+            fail("Expected failure for boolean + null");
+        } catch (JsonMappingException e) {
+            verifyException(e, "Cannot map `null` into type boolean");
+        }
+        // byte/char/short/int/long
+        try {
+            reader.readValue("{\"byteValue\":null}");
+            fail("Expected failure for byte + null");
+        } catch (JsonMappingException e) {
+            verifyException(e, "Cannot map `null` into type byte");
+        }
+        try {
+            reader.readValue("{\"charValue\":null}");
+            fail("Expected failure for char + null");
+        } catch (JsonMappingException e) {
+            verifyException(e, "Cannot map `null` into type char");
+        }
+        try {
+            reader.readValue("{\"shortValue\":null}");
+            fail("Expected failure for short + null");
+        } catch (JsonMappingException e) {
+            verifyException(e, "Cannot map `null` into type short");
+        }
+        try {
+            reader.readValue("{\"intValue\":null}");
+            fail("Expected failure for int + null");
+        } catch (JsonMappingException e) {
+            verifyException(e, "Cannot map `null` into type int");
+        }
+        try {
+            reader.readValue("{\"longValue\":null}");
+            fail("Expected failure for long + null");
+        } catch (JsonMappingException e) {
+            verifyException(e, "Cannot map `null` into type long");
+        }
+
+        // float/double
+        try {
+            reader.readValue("{\"floatValue\":null}");
+            fail("Expected failure for float + null");
+        } catch (JsonMappingException e) {
+            verifyException(e, "Cannot map `null` into type float");
+        }
+        try {
+            reader.readValue("{\"doubleValue\":null}");
+            fail("Expected failure for double + null");
+        } catch (JsonMappingException e) {
+            verifyException(e, "Cannot map `null` into type double");
+        }
+    }
+
+    public void testNullForPrimitiveArrays() throws IOException
+    {
+        _testNullForPrimitiveArrays(boolean[].class, Boolean.FALSE);
+        _testNullForPrimitiveArrays(byte[].class, Byte.valueOf((byte) 0));
+        _testNullForPrimitiveArrays(char[].class, Character.valueOf((char) 0), false);
+        _testNullForPrimitiveArrays(short[].class, Short.valueOf((short)0));
+        _testNullForPrimitiveArrays(int[].class, Integer.valueOf(0));
+        _testNullForPrimitiveArrays(long[].class, Long.valueOf(0L));
+        _testNullForPrimitiveArrays(float[].class, Float.valueOf(0f));
+        _testNullForPrimitiveArrays(double[].class, Double.valueOf(0d));
+    }
+
+    private void _testNullForPrimitiveArrays(Class<?> cls, Object defValue) throws IOException {
+        _testNullForPrimitiveArrays(cls, defValue, true);
+    }
+
+    private void _testNullForPrimitiveArrays(Class<?> cls, Object defValue,
+            boolean testEmptyString) throws IOException
+    {
+        final String EMPTY_STRING_JSON = "[ \"\" ]";
+        final String JSON_WITH_NULL = "[ null ]";
+        final String SIMPLE_NAME = "`"+cls.getSimpleName()+"`";
+        final ObjectReader readerCoerceOk = MAPPER.readerFor(cls);
+        final ObjectReader readerNoCoerce = readerCoerceOk
+                .with(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES);
+
+        Object ob = readerCoerceOk.forType(cls).readValue(JSON_WITH_NULL);
+        assertEquals(1, Array.getLength(ob));
+        assertEquals(defValue, Array.get(ob, 0));
+        try {
+            readerNoCoerce.readValue(JSON_WITH_NULL);
+            fail("Should not pass");
+        } catch (JsonMappingException e) {
+            verifyException(e, "Cannot coerce `null`");
+            verifyException(e, "as content of type "+SIMPLE_NAME);
+        }
+        
+        if (testEmptyString) {
+            ob = readerCoerceOk.forType(cls).readValue(EMPTY_STRING_JSON);
+            assertEquals(1, Array.getLength(ob));
+            assertEquals(defValue, Array.get(ob, 0));
+
+            try {
+                readerNoCoerce.readValue(EMPTY_STRING_JSON);
+                fail("Should not pass");
+            } catch (JsonMappingException e) {
+                verifyException(e, "Cannot coerce empty String (\"\")");
+                verifyException(e, "as content of type "+SIMPLE_NAME);
+            }
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Test for invalid String values
+    /**********************************************************
+     */
+
+    public void testInvalidStringCoercionFail() throws IOException
+    {
+        _testInvalidStringCoercionFail(boolean[].class);
+        _testInvalidStringCoercionFail(byte[].class);
+
+        // char[] is special, cannot use generalized test here
+//        _testInvalidStringCoercionFail(char[].class);
+        _testInvalidStringCoercionFail(short[].class);
+        _testInvalidStringCoercionFail(int[].class);
+        _testInvalidStringCoercionFail(long[].class);
+        _testInvalidStringCoercionFail(float[].class);
+        _testInvalidStringCoercionFail(double[].class);
+    }
+
+    private void _testInvalidStringCoercionFail(Class<?> cls) throws IOException
+    {
+        final String JSON = "[ \"foobar\" ]";
+        final String SIMPLE_NAME = cls.getSimpleName();
+
+        try {
+            MAPPER.readerFor(cls).readValue(JSON);
+            fail("Should not pass");
+        } catch (JsonMappingException e) {
+            verifyException(e, "Cannot deserialize value of type `"+SIMPLE_NAME+"` from String \"foobar\"");
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/JDKStringLikeTypesTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKStringLikeTypesTest.java
similarity index 78%
rename from src/test/java/com/fasterxml/jackson/databind/deser/JDKStringLikeTypesTest.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKStringLikeTypesTest.java
index cef00c1..7f65dd3 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/JDKStringLikeTypesTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKStringLikeTypesTest.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.deser;
+package com.fasterxml.jackson.databind.deser.jdk;
 
 import java.io.*;
 import java.net.*;
@@ -10,10 +10,11 @@
 import java.util.regex.Pattern;
 
 import com.fasterxml.jackson.annotation.*;
-import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
+
 import com.fasterxml.jackson.core.Base64Variants;
 import com.fasterxml.jackson.core.JsonParser;
 import com.fasterxml.jackson.core.JsonProcessingException;
+
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
 import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
@@ -55,7 +56,6 @@
             jp.skipChildren();
             return new StackTraceElement("a", "b", "b", StackTraceBean.NUM);
         }
-        
     }
 
     /*
@@ -112,41 +112,6 @@
         assertSame(String.class, result.clazz);
     }
 
-    public void testClassAsArray() throws Exception
-    {
-        final ObjectMapper mapper = new ObjectMapper();        
-        Class<?> result = mapper
-                    .readerFor(Class.class)
-                    .with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)
-                    .readValue(quote(String.class.getName()));
-        assertEquals(String.class, result);
-
-        try {
-            mapper
-                .readerFor(Class.class)
-                .without(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)
-                .readValue("[" + quote(String.class.getName()) + "]");
-            fail("Did not throw exception when UNWRAP_SINGLE_VALUE_ARRAYS feature was disabled and attempted to read a Class array containing one element");
-        } catch (JsonMappingException e) {
-            verifyException(e, "out of START_ARRAY token");
-        }
-
-        try {
-           mapper
-               .readerFor(Class.class)
-               .with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)
-               .readValue("[" + quote(Object.class.getName()) + "," + quote(Object.class.getName()) +"]"); 
-           fail("Did not throw exception when UNWRAP_SINGLE_VALUE_ARRAYS feature was enabled and attempted to read a Class array containing two elements");
-        } catch (JsonMappingException e) {
-            verifyException(e, "more than a single value in");
-        }               
-        result = mapper
-                .readerFor(Class.class)
-                .with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)
-                .readValue("[" + quote(String.class.getName()) + "]");
-        assertEquals(String.class, result);
-    }
-
     public void testCurrency() throws IOException
     {
         Currency usd = Currency.getInstance("USD");
@@ -163,13 +128,6 @@
         String json = MAPPER.writeValueAsString(abs);
         File result = MAPPER.readValue(json, File.class);
         assertEquals(abs, result.getAbsolutePath());
-
-        // Then #170
-        final ObjectMapper mapper2 = new ObjectMapper();
-        mapper2.setVisibility(PropertyAccessor.CREATOR, Visibility.NONE);
-
-        result = mapper2.readValue(json, File.class);
-        assertEquals(abs, result.getAbsolutePath());
     }
 
     public void testLocale() throws IOException
@@ -317,30 +275,6 @@
         }
     }
 
-    public void testURIAsArray() throws Exception
-    {
-        final ObjectReader reader = MAPPER.readerFor(URI.class);
-        final URI value = new URI("http://foo.com");
-        try {
-            reader.without(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)
-                .readValue("[\""+value.toString()+"\"]");
-            fail("Did not throw exception for single value array when UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
-        } catch (JsonMappingException e) {
-            verifyException(e, "out of START_ARRAY token");
-        }
-        
-        try {
-            reader.with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)
-                    .readValue("[\""+value.toString()+"\",\""+value.toString()+"\"]");
-            fail("Did not throw exception for single value array when there were multiple values");
-        } catch (JsonMappingException e) {
-            verifyException(e, "more than a single value in the array");
-        }
-        assertEquals(value,
-                reader.with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)
-                .readValue("[\""+value.toString()+"\"]"));
-    }
-
     public void testURL() throws Exception
     {
         URL exp = new URL("http://foo.com");
@@ -387,25 +321,6 @@
             UUID uuid = UUID.fromString(value);
             assertEquals(uuid,
                     mapper.readValue(quote(value), UUID.class));
-            
-            try {
-                mapper.readValue("[" + quote(value) + "]", UUID.class);
-                fail("Exception was not thrown when UNWRAP_SINGLE_VALUE_ARRAYS is disabled and attempted to read a single value array as a single element");
-            } catch (JsonMappingException exp) {
-                //Exception thrown successfully
-            }
-            
-            mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
-            
-            assertEquals(uuid,
-                    mapper.readValue("[" + quote(value) + "]", UUID.class));
-            
-            try {
-                mapper.readValue("[" + quote(value) + "," + quote(value) + "]", UUID.class);
-                fail("Exception was not thrown when UNWRAP_SINGLE_VALUE_ARRAYS is enabled and attempted to read a multi value array as a single element");
-            } catch (JsonMappingException exp) {
-                //Exception thrown successfully
-            }
         }
         // then use templating; note that these are not exactly valid UUIDs
         // wrt spec (type bits etc), but JDK UUID should deal ok
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestMapDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/MapDeserializationTest.java
similarity index 80%
rename from src/test/java/com/fasterxml/jackson/databind/deser/TestMapDeserialization.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/jdk/MapDeserializationTest.java
index 76d9ee6..852f389 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestMapDeserialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/MapDeserializationTest.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.deser;
+package com.fasterxml.jackson.databind.deser.jdk;
 
 import java.io.IOException;
 import java.text.DateFormat;
@@ -14,7 +14,7 @@
 import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
 
 @SuppressWarnings("serial")
-public class TestMapDeserialization
+public class MapDeserializationTest
     extends BaseMapTest
 {
     static enum Key {
@@ -28,18 +28,18 @@
         public BrokenMap(boolean dummy) { super(); }
     }
 
-    @JsonDeserialize(using=MapDeserializer.class)
+    @JsonDeserialize(using=CustomMapDeserializer.class)
     static class CustomMap extends LinkedHashMap<String,String> { }
 
-    static class MapDeserializer extends StdDeserializer<CustomMap>
+    static class CustomMapDeserializer extends StdDeserializer<CustomMap>
     {
-        public MapDeserializer() { super(CustomMap.class); }
+        public CustomMapDeserializer() { super(CustomMap.class); }
         @Override
-        public CustomMap deserialize(JsonParser jp, DeserializationContext ctxt)
+        public CustomMap deserialize(JsonParser p, DeserializationContext ctxt)
             throws IOException
         {
             CustomMap result = new CustomMap();
-            result.put("x", jp.getText());
+            result.put("x", p.getText());
             return result;
         }
     }
@@ -57,7 +57,6 @@
         }
     }
 
-    // [databind#142]
     public static class EnumMapContainer {
         @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="@class")
         public EnumMap<KeyEnum,ITestType> testTypes;
@@ -87,28 +86,6 @@
 
     private final ObjectMapper MAPPER = new ObjectMapper();
 
-    public void testUntypedMap() throws Exception
-    {
-        // to get "untyped" default map-to-map, pass Object.class
-        String JSON = "{ \"foo\" : \"bar\", \"crazy\" : true, \"null\" : null }";
-
-        // Not a guaranteed cast theoretically, but will work:
-        @SuppressWarnings("unchecked")
-        Map<String,Object> result = (Map<String,Object>)MAPPER.readValue(JSON, Object.class);
-        assertNotNull(result);
-        assertTrue(result instanceof Map<?,?>);
-
-        assertEquals(3, result.size());
-
-        assertEquals("bar", result.get("foo"));
-        assertEquals(Boolean.TRUE, result.get("crazy"));
-        assertNull(result.get("null"));
-
-        // Plus, non existing:
-        assertNull(result.get("bar"));
-        assertNull(result.get(3));
-    }
-
     public void testBigUntypedMap() throws Exception
     {
         Map<String,Object> map = new LinkedHashMap<String,Object>();
@@ -246,8 +223,8 @@
     {
         // to get typing, must use type reference
         String JSON = "{ \"1\" : true, \"-1\" : false }";
-        Map<String,Integer> result = MAPPER.readValue
-            (JSON, new TypeReference<HashMap<Integer,Boolean>>() { });
+        Map<?,Object> result = MAPPER.readValue
+            (JSON, new TypeReference<HashMap<Integer,Object>>() { });
 
         assertNotNull(result);
         assertEquals(HashMap.class, result.getClass());
@@ -504,78 +481,6 @@
 
     /*
     /**********************************************************
-    /* Test methods, annotated Map.Entry
-    /**********************************************************
-     */
-
-    public void testMapEntrySimpleTypes() throws Exception
-    {
-        List<Map.Entry<String,Long>> stuff = MAPPER.readValue(aposToQuotes("[{'a':15},{'b':42}]"),
-                new TypeReference<List<Map.Entry<String,Long>>>() { });
-        assertNotNull(stuff);
-        assertEquals(2, stuff.size());
-        assertNotNull(stuff.get(1));
-        assertEquals("b", stuff.get(1).getKey());
-        assertEquals(Long.valueOf(42), stuff.get(1).getValue());
-    }
-
-    public void testMapEntryWithStringBean() throws Exception
-    {
-        List<Map.Entry<Integer,StringWrapper>> stuff = MAPPER.readValue(aposToQuotes("[{'28':'Foo'},{'13':'Bar'}]"),
-                new TypeReference<List<Map.Entry<Integer,StringWrapper>>>() { });
-        assertNotNull(stuff);
-        assertEquals(2, stuff.size());
-        assertNotNull(stuff.get(1));
-        assertEquals(Integer.valueOf(13), stuff.get(1).getKey());
-        
-        StringWrapper sw = stuff.get(1).getValue();
-        assertEquals("Bar", sw.str);
-    }
-
-    public void testMapEntryFail() throws Exception
-    {
-        try {
-            /*List<Map.Entry<Integer,StringWrapper>> stuff =*/ MAPPER.readValue(aposToQuotes("[{'28':'Foo','13':'Bar'}]"),
-                    new TypeReference<List<Map.Entry<Integer,StringWrapper>>>() { });
-            fail("Should not have passed");
-        } catch (Exception e) {
-            verifyException(e, "more than one entry in JSON");
-        }
-    }
-
-    /*
-    /**********************************************************
-    /* Test methods, other exotic Map types
-    /**********************************************************
-     */
-    
-    // [databind#810]
-    public void testReadProperties() throws Exception
-    {
-        Properties props = MAPPER.readValue(aposToQuotes("{'a':'foo', 'b':123, 'c':true}"),
-                Properties.class);
-        assertEquals(3, props.size());
-        assertEquals("foo", props.getProperty("a"));
-        assertEquals("123", props.getProperty("b"));
-        assertEquals("true", props.getProperty("c"));
-    }
-
-    // JDK singletonMap
-    public void testSingletonMapRoundtrip() throws Exception
-    {
-        final TypeReference<?> type = new TypeReference<Map<String,IntWrapper>>() { };
-
-        String json = MAPPER.writeValueAsString(Collections.singletonMap("value", new IntWrapper(5)));
-        Map<String,IntWrapper> result = MAPPER.readValue(json, type);
-        assertNotNull(result);
-        assertEquals(1, result.size());
-        IntWrapper w = result.get("value");
-        assertNotNull(w);
-        assertEquals(5, w.i);
-    }
-
-    /*
-    /**********************************************************
     /* Error tests
     /**********************************************************
      */
@@ -584,7 +489,7 @@
     {
         try {
             Object result = MAPPER.readValue("[ 1, 2 ]", 
-                                             new TypeReference<Map<String,String>>() { });
+                    new TypeReference<Map<String,String>>() { });
             fail("Expected an exception, but got result value: "+result);
         } catch (JsonMappingException jex) {
             verifyException(jex, "START_ARRAY");
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/MapKeyDeserializationTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/MapKeyDeserializationTest.java
new file mode 100644
index 0000000..32a616c
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/MapKeyDeserializationTest.java
@@ -0,0 +1,141 @@
+package com.fasterxml.jackson.databind.deser.jdk;
+
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+import com.fasterxml.jackson.core.Base64Variants;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.*;
+
+import org.junit.Assert;
+
+public class MapKeyDeserializationTest extends BaseMapTest
+{
+    static class FullName {
+        private String _firstname, _lastname;
+
+        private FullName(String firstname, String lastname) {
+            _firstname = firstname;
+            _lastname = lastname;
+        }
+
+        @JsonCreator
+        public static FullName valueOf(String value) {
+            String[] mySplit = value.split("\\.");
+            return new FullName(mySplit[0], mySplit[1]);
+        }
+
+        public static FullName valueOf(String firstname, String lastname) {
+            return new FullName(firstname, lastname);
+        }
+
+        @JsonValue
+        @Override
+        public String toString() {
+            return _firstname + "." + _lastname;
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods, wrapper keys
+    /**********************************************************
+     */
+
+    final private ObjectMapper MAPPER = objectMapper();
+
+    public void testBooleanMapKeyDeserialization() throws Exception
+    {
+        TypeReference<?> type = new TypeReference<MapWrapper<Boolean, String>>() { };
+        MapWrapper<byte[], String> result = MAPPER.readValue(aposToQuotes("{'map':{'true':'foobar'}}"), type);
+                
+        assertEquals(1, result.map.size());
+        Assert.assertEquals(Boolean.TRUE, result.map.entrySet().iterator().next().getKey());
+
+        result = MAPPER.readValue(aposToQuotes("{'map':{'false':'foobar'}}"), type);
+        assertEquals(1, result.map.size());
+        Assert.assertEquals(Boolean.FALSE, result.map.entrySet().iterator().next().getKey());
+    }
+
+    public void testByteMapKeyDeserialization() throws Exception
+    {
+        TypeReference<?> type = new TypeReference<MapWrapper<Byte, String>>() { };
+        MapWrapper<byte[], String> result = MAPPER.readValue(aposToQuotes("{'map':{'13':'foobar'}}"), type);
+        assertEquals(1, result.map.size());
+        Assert.assertEquals(Byte.valueOf((byte) 13), result.map.entrySet().iterator().next().getKey());
+    }
+
+    public void testShortMapKeyDeserialization() throws Exception
+    {
+        TypeReference<?> type = new TypeReference<MapWrapper<Short, String>>() { };
+        MapWrapper<byte[], String> result = MAPPER.readValue(aposToQuotes("{'map':{'13':'foobar'}}"), type);
+        assertEquals(1, result.map.size());
+        Assert.assertEquals(Short.valueOf((short) 13), result.map.entrySet().iterator().next().getKey());
+    }
+
+    public void testIntegerMapKeyDeserialization() throws Exception
+    {
+        TypeReference<?> type = new TypeReference<MapWrapper<Integer, String>>() { };
+        MapWrapper<byte[], String> result = MAPPER.readValue(aposToQuotes("{'map':{'-3':'foobar'}}"), type);
+        assertEquals(1, result.map.size());
+        Assert.assertEquals(Integer.valueOf(-3), result.map.entrySet().iterator().next().getKey());
+    }
+
+    public void testLongMapKeyDeserialization() throws Exception
+    {
+        TypeReference<?> type = new TypeReference<MapWrapper<Long, String>>() { };
+        MapWrapper<byte[], String> result = MAPPER.readValue(aposToQuotes("{'map':{'42':'foobar'}}"), type);
+        assertEquals(1, result.map.size());
+        Assert.assertEquals(Long.valueOf(42), result.map.entrySet().iterator().next().getKey());
+    }
+
+    public void testFloatMapKeyDeserialization() throws Exception
+    {
+        TypeReference<?> type = new TypeReference<MapWrapper<Float, String>>() { };
+        MapWrapper<byte[], String> result = MAPPER.readValue(aposToQuotes("{'map':{'3.5':'foobar'}}"), type);
+        assertEquals(1, result.map.size());
+        Assert.assertEquals(Float.valueOf(3.5f), result.map.entrySet().iterator().next().getKey());
+    }
+
+    public void testDoubleMapKeyDeserialization() throws Exception
+    {
+        TypeReference<?> type = new TypeReference<MapWrapper<Double, String>>() { };
+        MapWrapper<byte[], String> result = MAPPER.readValue(aposToQuotes("{'map':{'0.25':'foobar'}}"), type);
+        assertEquals(1, result.map.size());
+        Assert.assertEquals(Double.valueOf(0.25), result.map.entrySet().iterator().next().getKey());
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods, other
+    /**********************************************************
+     */
+
+    public void testDeserializeKeyViaFactory() throws Exception
+    {
+        Map<FullName, Double> map =
+            MAPPER.readValue("{\"first.last\": 42}",
+                    new TypeReference<Map<FullName, Double>>() { });
+        Map.Entry<FullName, Double> entry = map.entrySet().iterator().next();
+        FullName key = entry.getKey();
+        assertEquals(key._firstname, "first");
+        assertEquals(key._lastname, "last");
+        assertEquals(entry.getValue().doubleValue(), 42, 0);
+    }
+
+    public void testByteArrayMapKeyDeserialization() throws Exception
+    {
+        byte[] binary = new byte[] { 1, 2, 4, 8, 16, 33, 79 };
+        String encoded = Base64Variants.MIME.encode(binary);
+
+        MapWrapper<byte[], String> result = MAPPER.readValue(
+                aposToQuotes("{'map':{'"+encoded+"':'foobar'}}"),
+                new TypeReference<MapWrapper<byte[], String>>() { });
+        assertEquals(1, result.map.size());
+        Map.Entry<byte[],String> entry = result.map.entrySet().iterator().next();
+        assertEquals("foobar", entry.getValue());
+        byte[] key = entry.getKey();
+        Assert.assertArrayEquals(binary, key);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/MapRelatedTypesDeserTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/MapRelatedTypesDeserTest.java
new file mode 100644
index 0000000..bc8dc16
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/MapRelatedTypesDeserTest.java
@@ -0,0 +1,84 @@
+package com.fasterxml.jackson.databind.deser.jdk;
+
+import java.util.*;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.*;
+
+public class MapRelatedTypesDeserTest
+    extends BaseMapTest
+{
+    private final ObjectMapper MAPPER = new ObjectMapper();
+
+    /*
+    /**********************************************************
+    /* Test methods, Map.Entry
+    /**********************************************************
+     */
+
+    public void testMapEntrySimpleTypes() throws Exception
+    {
+        List<Map.Entry<String,Long>> stuff = MAPPER.readValue(aposToQuotes("[{'a':15},{'b':42}]"),
+                new TypeReference<List<Map.Entry<String,Long>>>() { });
+        assertNotNull(stuff);
+        assertEquals(2, stuff.size());
+        assertNotNull(stuff.get(1));
+        assertEquals("b", stuff.get(1).getKey());
+        assertEquals(Long.valueOf(42), stuff.get(1).getValue());
+    }
+
+    public void testMapEntryWithStringBean() throws Exception
+    {
+        List<Map.Entry<Integer,StringWrapper>> stuff = MAPPER.readValue(aposToQuotes("[{'28':'Foo'},{'13':'Bar'}]"),
+                new TypeReference<List<Map.Entry<Integer,StringWrapper>>>() { });
+        assertNotNull(stuff);
+        assertEquals(2, stuff.size());
+        assertNotNull(stuff.get(1));
+        assertEquals(Integer.valueOf(13), stuff.get(1).getKey());
+        
+        StringWrapper sw = stuff.get(1).getValue();
+        assertEquals("Bar", sw.str);
+    }
+
+    public void testMapEntryFail() throws Exception
+    {
+        try {
+            /*List<Map.Entry<Integer,StringWrapper>> stuff =*/ MAPPER.readValue(aposToQuotes("[{'28':'Foo','13':'Bar'}]"),
+                    new TypeReference<List<Map.Entry<Integer,StringWrapper>>>() { });
+            fail("Should not have passed");
+        } catch (Exception e) {
+            verifyException(e, "more than one entry in JSON");
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods, other exotic Map types
+    /**********************************************************
+     */
+    
+    // [databind#810]
+    public void testReadProperties() throws Exception
+    {
+        Properties props = MAPPER.readValue(aposToQuotes("{'a':'foo', 'b':123, 'c':true}"),
+                Properties.class);
+        assertEquals(3, props.size());
+        assertEquals("foo", props.getProperty("a"));
+        assertEquals("123", props.getProperty("b"));
+        assertEquals("true", props.getProperty("c"));
+    }
+
+    // JDK singletonMap
+    public void testSingletonMapRoundtrip() throws Exception
+    {
+        final TypeReference<?> type = new TypeReference<Map<String,IntWrapper>>() { };
+
+        String json = MAPPER.writeValueAsString(Collections.singletonMap("value", new IntWrapper(5)));
+        Map<String,IntWrapper> result = MAPPER.readValue(json, type);
+        assertNotNull(result);
+        assertEquals(1, result.size());
+        IntWrapper w = result.get("value");
+        assertNotNull(w);
+        assertEquals(5, w.i);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestDefaultForUtilCollections1868.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/TestDefaultForUtilCollections1868.java
similarity index 83%
rename from src/test/java/com/fasterxml/jackson/failing/TestDefaultForUtilCollections1868.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/jdk/TestDefaultForUtilCollections1868.java
index 4dc9cd1..f6dd938 100644
--- a/src/test/java/com/fasterxml/jackson/failing/TestDefaultForUtilCollections1868.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/TestDefaultForUtilCollections1868.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.failing;
+package com.fasterxml.jackson.databind.deser.jdk;
 
 import java.util.*;
 
@@ -76,6 +76,22 @@
 
    /*
    /**********************************************************
+   /* Unit tests, other
+   /**********************************************************
+    */
+
+   public void testArraysAsList() throws Exception
+   {
+       // Here there are no semantics to preserve, so simply check that
+       // contents remain the same
+       List<String> input = Arrays.asList("a", "bc", "def");
+       String json = DEFAULT_MAPPER.writeValueAsString(input);
+       List<?> result = DEFAULT_MAPPER.readValue(json, List.class);
+       assertEquals(input, result);
+   }
+
+   /*
+   /**********************************************************
    /* Helper methods
    /**********************************************************
     */
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestUntypedDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/UntypedDeserializationTest.java
similarity index 68%
rename from src/test/java/com/fasterxml/jackson/databind/deser/TestUntypedDeserialization.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/jdk/UntypedDeserializationTest.java
index eaf2bc8..3b28b9d 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestUntypedDeserialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/UntypedDeserializationTest.java
@@ -1,13 +1,17 @@
-package com.fasterxml.jackson.databind.deser;
+package com.fasterxml.jackson.databind.deser.jdk;
 
 import java.io.*;
+import java.math.BigDecimal;
+import java.math.BigInteger;
 import java.util.*;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
 import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
 import com.fasterxml.jackson.core.*;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
+import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
 import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
 import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer;
 import com.fasterxml.jackson.databind.module.SimpleModule;
@@ -17,7 +21,7 @@
  * one that only uses core JDK types; wrappers, Maps and Lists.
  */
 @SuppressWarnings("serial")
-public class TestUntypedDeserialization
+public class UntypedDeserializationTest
     extends BaseMapTest
 {
     static class UCStringDeserializer
@@ -26,8 +30,8 @@
         public UCStringDeserializer() { super(String.class); }
 
         @Override
-        public String deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
-            return jp.getText().toUpperCase();
+        public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
+            return p.getText().toUpperCase();
         }
     }
 
@@ -42,7 +46,7 @@
         }
 
         @Override
-        public Number deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
+        public Number deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
             return value;
         }
     }
@@ -54,12 +58,12 @@
         public ListDeserializer() { super(List.class); }
 
         @Override
-        public List<Object> deserialize(JsonParser jp, DeserializationContext ctxt)
+        public List<Object> deserialize(JsonParser p, DeserializationContext ctxt)
             throws IOException
         {
             ArrayList<Object> list = new ArrayList<Object>();
-            while (jp.nextValue() != JsonToken.END_ARRAY) {
-                list.add("X"+jp.getText());
+            while (p.nextValue() != JsonToken.END_ARRAY) {
+                list.add("X"+p.getText());
             }
             return list;
         }
@@ -76,17 +80,17 @@
         }
     }
 
-    static class MapDeserializer extends StdDeserializer<Map<String,Object>>
+    static class YMapDeserializer extends StdDeserializer<Map<String,Object>>
     {
-        public MapDeserializer() { super(Map.class); }
+        public YMapDeserializer() { super(Map.class); }
 
         @Override
-        public Map<String,Object> deserialize(JsonParser jp, DeserializationContext ctxt)
+        public Map<String,Object> deserialize(JsonParser p, DeserializationContext ctxt)
             throws IOException
         {
             Map<String,Object> map = new LinkedHashMap<String,Object>();
-            while (jp.nextValue() != JsonToken.END_OBJECT) {
-                map.put(jp.getCurrentName(), "Y"+jp.getText());
+            while (p.nextValue() != JsonToken.END_OBJECT) {
+                map.put(p.getCurrentName(), "Y"+p.getText());
             }
             return map;
         }
@@ -101,6 +105,11 @@
         }
     }
 
+    static class WrappedPolymorphicUntyped {
+        @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS)
+        public Object value;
+    }
+
     static class WrappedUntyped1460 {
         public Object value;
     }
@@ -166,7 +175,30 @@
 
         // and that's all folks!
     }
-    
+
+    @SuppressWarnings("unlikely-arg-type")
+    public void testUntypedMap() throws Exception
+    {
+        // to get "untyped" default map-to-map, pass Object.class
+        String JSON = "{ \"foo\" : \"bar\", \"crazy\" : true, \"null\" : null }";
+
+        // Not a guaranteed cast theoretically, but will work:
+        @SuppressWarnings("unchecked")
+        Map<String,Object> result = (Map<String,Object>)MAPPER.readValue(JSON, Object.class);
+        assertNotNull(result);
+        assertTrue(result instanceof Map<?,?>);
+
+        assertEquals(3, result.size());
+
+        assertEquals("bar", result.get("foo"));
+        assertEquals(Boolean.TRUE, result.get("crazy"));
+        assertNull(result.get("null"));
+
+        // Plus, non existing:
+        assertNull(result.get("bar"));
+        assertNull(result.get(3));
+    }
+
     public void testNestedUntypes() throws IOException
     {
         // 05-Apr-2014, tatu: Odd failures if using shared mapper; so work around:
@@ -223,6 +255,62 @@
         assertEquals(Integer.valueOf(13), value);
     }
 
+    // Test that exercises non-vanilla variant, with just one simple custom deserializer
+    public void testNonVanilla() throws IOException
+    {
+        SimpleModule m = new SimpleModule("test-module");
+        m.addDeserializer(String.class, new UCStringDeserializer());
+        final ObjectMapper mapper = new ObjectMapper()
+            .registerModule(m);
+
+        // Also: since this is now non-vanilla variant, try more alternatives
+        List<?> l = (List<?>) mapper.readValue("[ true, false, 7, 0.5, \"foo\"]", Object.class);
+        assertEquals(5, l.size());
+        assertEquals(Boolean.TRUE, l.get(0));
+        assertEquals(Boolean.FALSE, l.get(1));
+        assertEquals(Integer.valueOf(7), l.get(2));
+        assertEquals(Double.valueOf(0.5), l.get(3));
+        assertEquals("FOO", l.get(4));
+
+        l = (List<?>) mapper.readValue("[ {}, [] ]", Object.class);
+        assertEquals(2, l.size());
+        assertTrue(l.get(0) instanceof Map<?,?>);
+        assertTrue(l.get(1) instanceof List<?>);
+
+        ObjectReader rDefault = mapper.readerFor(WrappedPolymorphicUntyped.class);
+        ObjectReader rAlt = rDefault
+                .with(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS,
+                        DeserializationFeature.USE_BIG_INTEGER_FOR_INTS);
+        WrappedPolymorphicUntyped w;
+
+        w = rDefault.readValue(aposToQuotes("{'value':10}"));
+        assertEquals(Integer.valueOf(10), w.value);
+        w = rAlt.readValue(aposToQuotes("{'value':10}"));
+        assertEquals(BigInteger.TEN, w.value);
+
+        w = rDefault.readValue(aposToQuotes("{'value':5.0}"));
+        assertEquals(Double.valueOf(5.0), w.value);
+        w = rAlt.readValue(aposToQuotes("{'value':5.0}"));
+        assertEquals(new BigDecimal("5.0"), w.value);
+
+        StringBuilder sb = new StringBuilder(100).append("[0");
+        for (int i = 1; i < 100; ++i) {
+            sb.append(", ").append(i);
+        }
+        sb.append("]");
+        final String INT_ARRAY_JSON = sb.toString();
+
+        // First read as-is, no type wrapping
+        Object ob = mapper.readerFor(Object.class)
+                .with(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY)
+                .readValue(INT_ARRAY_JSON);
+        assertTrue(ob instanceof Object[]);
+        Object[] obs = (Object[]) ob;
+        for (int i = 0; i < 100; ++i) {
+            assertEquals(Integer.valueOf(i), obs[i]);
+        }
+    }
+
     public void testUntypedWithListDeser() throws IOException
     {
         SimpleModule m = new SimpleModule("test-module");
@@ -243,7 +331,7 @@
     public void testUntypedWithMapDeser() throws IOException
     {
         SimpleModule m = new SimpleModule("test-module");
-        m.addDeserializer(Map.class, new MapDeserializer());
+        m.addDeserializer(Map.class, new YMapDeserializer());
         final ObjectMapper mapper = new ObjectMapper()
             .registerModule(m);
 
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/merge/ArrayMergeTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/merge/ArrayMergeTest.java
new file mode 100644
index 0000000..908ddc5
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/merge/ArrayMergeTest.java
@@ -0,0 +1,161 @@
+package com.fasterxml.jackson.databind.deser.merge;
+
+import org.junit.Assert;
+
+import com.fasterxml.jackson.annotation.JsonMerge;
+import com.fasterxml.jackson.annotation.OptBoolean;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+
+import com.fasterxml.jackson.databind.*;
+
+public class ArrayMergeTest extends BaseMapTest
+{
+    static class MergedX<T>
+    {
+        @JsonMerge(OptBoolean.TRUE)
+        public T value;
+
+        public MergedX(T v) { value = v; }
+        protected MergedX() { }
+    }
+    
+    /*
+    /********************************************************
+    /* Test methods
+    /********************************************************
+     */
+
+    private final ObjectMapper MAPPER = newObjectMapper()
+            // 26-Oct-2016, tatu: Make sure we'll report merge problems by default
+            .disable(MapperFeature.IGNORE_MERGE_FOR_UNMERGEABLE)
+    ;
+
+    public void testObjectArrayMerging() throws Exception
+    {
+        MergedX<Object[]> input = new MergedX<Object[]>(new Object[] {
+                "foo"
+        });
+        final JavaType type = MAPPER.getTypeFactory().constructType(new TypeReference<MergedX<Object[]>>() {});
+        MergedX<Object[]> result = MAPPER.readerFor(type)
+                .withValueToUpdate(input)
+                .readValue(aposToQuotes("{'value':['bar']}"));
+        assertSame(input, result);
+        assertEquals(2, result.value.length);
+        assertEquals("foo", result.value[0]);
+        assertEquals("bar", result.value[1]);
+
+        // and with one trick
+        result = MAPPER.readerFor(type)
+                .with(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
+                .withValueToUpdate(input)
+                .readValue(aposToQuotes("{'value':'zap'}"));
+        assertSame(input, result);
+        assertEquals(3, result.value.length);
+        assertEquals("foo", result.value[0]);
+        assertEquals("bar", result.value[1]);
+        assertEquals("zap", result.value[2]);
+    }
+
+    public void testStringArrayMerging() throws Exception
+    {
+        MergedX<String[]> input = new MergedX<String[]>(new String[] { "foo" });
+        MergedX<String[]> result = MAPPER
+                .readerFor(new TypeReference<MergedX<String[]>>() {})
+                .withValueToUpdate(input)
+                .readValue(aposToQuotes("{'value':['bar']}"));
+        assertSame(input, result);
+        assertEquals(2, result.value.length);
+        assertEquals("foo", result.value[0]);
+        assertEquals("bar", result.value[1]);
+    }
+
+    public void testBooleanArrayMerging() throws Exception
+    {
+        MergedX<boolean[]> input = new MergedX<boolean[]>(new boolean[] { true, false });
+        MergedX<boolean[]> result = MAPPER
+                .readerFor(new TypeReference<MergedX<boolean[]>>() {})
+                .withValueToUpdate(input)
+                .readValue(aposToQuotes("{'value':[true]}"));
+        assertSame(input, result);
+        assertEquals(3, result.value.length);
+        Assert.assertArrayEquals(new boolean[] { true, false, true }, result.value);
+    }
+
+    public void testByteArrayMerging() throws Exception
+    {
+        MergedX<byte[]> input = new MergedX<byte[]>(new byte[] { 1, 2 });
+        MergedX<byte[]> result = MAPPER
+                .readerFor(new TypeReference<MergedX<byte[]>>() {})
+                .withValueToUpdate(input)
+                .readValue(aposToQuotes("{'value':[4, 6.0, null]}"));
+        assertSame(input, result);
+        assertEquals(5, result.value.length);
+        Assert.assertArrayEquals(new byte[] { 1, 2, 4, 6, 0 }, result.value);
+    }
+
+    public void testShortArrayMerging() throws Exception
+    {
+        MergedX<short[]> input = new MergedX<short[]>(new short[] { 1, 2 });
+        MergedX<short[]> result = MAPPER
+                .readerFor(new TypeReference<MergedX<short[]>>() {})
+                .withValueToUpdate(input)
+                .readValue(aposToQuotes("{'value':[4, 6]}"));
+        assertSame(input, result);
+        assertEquals(4, result.value.length);
+        Assert.assertArrayEquals(new short[] { 1, 2, 4, 6 }, result.value);
+    }
+
+    public void testCharArrayMerging() throws Exception
+    {
+        MergedX<char[]> input = new MergedX<char[]>(new char[] { 'a', 'b' });
+        MergedX<char[]> result = MAPPER
+                .readerFor(new TypeReference<MergedX<char[]>>() {})
+                .withValueToUpdate(input)
+                .readValue(aposToQuotes("{'value':['c']}"));
+        assertSame(input, result);
+        Assert.assertArrayEquals(new char[] { 'a', 'b', 'c' }, result.value);
+
+        // also some variation
+        input = new MergedX<char[]>(new char[] { });
+        result = MAPPER
+                .readerFor(new TypeReference<MergedX<char[]>>() {})
+                .withValueToUpdate(input)
+                .readValue(aposToQuotes("{'value':['c']}"));
+        assertSame(input, result);
+        Assert.assertArrayEquals(new char[] { 'c' }, result.value);
+    }
+    
+    public void testIntArrayMerging() throws Exception
+    {
+        MergedX<int[]> input = new MergedX<int[]>(new int[] { 1, 2 });
+        MergedX<int[]> result = MAPPER
+                .readerFor(new TypeReference<MergedX<int[]>>() {})
+                .withValueToUpdate(input)
+                .readValue(aposToQuotes("{'value':[4, 6]}"));
+        assertSame(input, result);
+        assertEquals(4, result.value.length);
+        Assert.assertArrayEquals(new int[] { 1, 2, 4, 6 }, result.value);
+
+        // also some variation
+        input = new MergedX<int[]>(new int[] { 3, 4, 6 });
+        result = MAPPER
+                .readerFor(new TypeReference<MergedX<int[]>>() {})
+                .withValueToUpdate(input)
+                .readValue(aposToQuotes("{'value':[ ]}"));
+        assertSame(input, result);
+        Assert.assertArrayEquals(new int[] { 3, 4, 6 }, result.value);
+    }
+
+    public void testLongArrayMerging() throws Exception
+    {
+        MergedX<long[]> input = new MergedX<long[]>(new long[] { 1, 2 });
+        MergedX<long[]> result = MAPPER
+                .readerFor(new TypeReference<MergedX<long[]>>() {})
+                .withValueToUpdate(input)
+                .readValue(aposToQuotes("{'value':[4, 6]}"));
+        assertSame(input, result);
+        assertEquals(4, result.value.length);
+        Assert.assertArrayEquals(new long[] { 1, 2, 4, 6 }, result.value);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/merge/CollectionMergeTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/merge/CollectionMergeTest.java
new file mode 100644
index 0000000..3305d37
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/merge/CollectionMergeTest.java
@@ -0,0 +1,102 @@
+package com.fasterxml.jackson.databind.deser.merge;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.JsonMerge;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.MapperFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class CollectionMergeTest extends BaseMapTest
+{
+    static class CollectionWrapper {
+        @JsonMerge
+        public Collection<String> bag = new TreeSet<String>();
+        {
+            bag.add("a");
+        }
+    }
+
+    static class MergedList
+    {
+        @JsonMerge
+        public List<String> values = new ArrayList<>();
+        {
+            values.add("a");
+        }
+    }
+
+    static class MergedEnumSet
+    {
+        @JsonMerge
+        public EnumSet<ABC> abc = EnumSet.of(ABC.B);
+    }
+
+    static class MergedX<T>
+    {
+        @JsonMerge
+        T value;
+
+        public MergedX(T v) { value = v; }
+        protected MergedX() { }
+
+        public void setValue(T v) { value = v; }
+    }
+
+    /*
+    /********************************************************
+    /* Test methods
+    /********************************************************
+     */
+
+    private final ObjectMapper MAPPER = newObjectMapper()
+            // 26-Oct-2016, tatu: Make sure we'll report merge problems by default
+            .disable(MapperFeature.IGNORE_MERGE_FOR_UNMERGEABLE)
+    ;
+
+    public void testCollectionMerging() throws Exception
+    {
+        CollectionWrapper w = MAPPER.readValue(aposToQuotes("{'bag':['b']}"), CollectionWrapper.class);
+        assertEquals(2, w.bag.size());
+        assertTrue(w.bag.contains("a"));
+        assertTrue(w.bag.contains("b"));
+    }
+
+    public void testListMerging() throws Exception
+    {
+        MergedList w = MAPPER.readValue(aposToQuotes("{'values':['x']}"), MergedList.class);
+        assertEquals(2, w.values.size());
+        assertTrue(w.values.contains("a"));
+        assertTrue(w.values.contains("x"));
+    }
+
+    // Test that uses generic type
+    public void testGenericListMerging() throws Exception
+    {
+        Collection<String> l = new ArrayList<>();
+        l.add("foo");
+        MergedX<Collection<String>> input = new MergedX<Collection<String>>(l);
+
+        MergedX<Collection<String>> result = MAPPER
+                .readerFor(new TypeReference<MergedX<Collection<String>>>() {})
+                .withValueToUpdate(input)
+                .readValue(aposToQuotes("{'value':['bar']}"));
+        assertSame(input, result);
+        assertEquals(2, result.value.size());
+        Iterator<String> it = result.value.iterator();
+        assertEquals("foo", it.next());
+        assertEquals("bar", it.next());
+    }
+
+    public void testEnumSetMerging() throws Exception
+    {
+        MergedEnumSet result = MAPPER.readValue(aposToQuotes("{'abc':['A']}"), MergedEnumSet.class);
+        assertEquals(2, result.abc.size());
+        assertTrue(result.abc.contains(ABC.B)); // original
+        assertTrue(result.abc.contains(ABC.A)); // added
+    }
+
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/merge/MapMerge1844Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/merge/MapMerge1844Test.java
new file mode 100644
index 0000000..e5fd75d
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/merge/MapMerge1844Test.java
@@ -0,0 +1,69 @@
+package com.fasterxml.jackson.databind.deser.merge;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+// for [databind#1844]
+public class MapMerge1844Test extends BaseMapTest
+{
+    static class TestMap1844 {
+        public Map<String, Integer> getMapStringInteger() {
+            return mapStringInteger;
+        }
+
+        @JsonProperty("key1")
+        public void setMapStringInteger(Map<String, Integer> mapStringInteger) {
+            this.mapStringInteger = mapStringInteger;
+        }
+
+        public Map<Integer, Integer> getMapIntegerInteger() {
+            return mapIntegerInteger;
+        }
+
+        @JsonProperty("key2")
+        public void setMapIntegerInteger(Map<Integer, Integer> mapIntegerInteger) {
+            this.mapIntegerInteger = mapIntegerInteger;
+        }
+
+        private Map<String, Integer> mapStringInteger = new LinkedHashMap<>();
+
+        private Map<Integer, Integer> mapIntegerInteger = new LinkedHashMap<>();
+    }
+
+    // for [databind#1844]
+    public void testMap1844() throws Exception
+    {
+        final ObjectMapper mapper = newObjectMapper();
+        mapper.setDefaultMergeable(true);
+
+        final String f1 = aposToQuotes(
+"{ 'key1' : {\n"
++"  '1': 1, '2': 2, '3': 3\n"
++"}, 'key2': {\n"
++"  '1': 1, '2': 2, '3': 3\n"
++"} }"
+);
+        final String f2 = aposToQuotes(
+"{ 'key1' : {\n"
++"  '1': 2, '2': 3, '4': 5\n"
++"}, 'key2': {\n"
++"  '1': 2, '2': 3, '4': 5\n"
++"} }"
+);
+        TestMap1844 testMap = mapper.readerFor(TestMap1844.class).readValue(f1);
+        testMap = mapper.readerForUpdating(testMap).readValue(f2);
+
+        assertEquals(Integer.valueOf(2), testMap.getMapStringInteger().get("1"));
+        assertEquals(Integer.valueOf(3), testMap.getMapStringInteger().get("2"));
+        assertEquals(Integer.valueOf(3), testMap.getMapStringInteger().get("3"));
+        assertEquals(Integer.valueOf(5), testMap.getMapStringInteger().get("4"));
+
+        assertEquals(Integer.valueOf(2), testMap.getMapIntegerInteger().get(1));
+        assertEquals(Integer.valueOf(3), testMap.getMapIntegerInteger().get(2));
+        assertEquals(Integer.valueOf(3), testMap.getMapIntegerInteger().get(3));
+        assertEquals(Integer.valueOf(5), testMap.getMapIntegerInteger().get(4));
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/merge/MapMergeTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/merge/MapMergeTest.java
new file mode 100644
index 0000000..647d934
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/merge/MapMergeTest.java
@@ -0,0 +1,215 @@
+package com.fasterxml.jackson.databind.deser.merge;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.JsonMerge;
+import com.fasterxml.jackson.annotation.JsonSetter;
+import com.fasterxml.jackson.annotation.Nulls;
+import com.fasterxml.jackson.databind.*;
+
+public class MapMergeTest extends BaseMapTest
+{
+    static class MergedMap
+    {
+        @JsonMerge
+        public Map<String,Object> values;
+
+        protected MergedMap() {
+            values = new LinkedHashMap<>();
+            values.put("a", "x");
+        }
+
+        public MergedMap(String a, String b) {
+            values = new LinkedHashMap<>();
+            values.put(a, b);
+        }
+
+        public MergedMap(Map<String,Object> src) {
+            values = src;
+        }
+    }
+
+    static class MergedIntMap
+    {
+        @JsonMerge
+        public Map<Integer,Object> values;
+
+        protected MergedIntMap() {
+            values = new LinkedHashMap<>();
+            values.put(Integer.valueOf(13), "a");
+        }
+    }
+
+    /*
+    /********************************************************
+    /* Test methods, Map merging
+    /********************************************************
+     */
+
+    private final ObjectMapper MAPPER = newObjectMapper()
+            // 26-Oct-2016, tatu: Make sure we'll report merge problems by default
+            .disable(MapperFeature.IGNORE_MERGE_FOR_UNMERGEABLE)
+    ;
+
+    private final ObjectMapper MAPPER_SKIP_NULLS = newObjectMapper()
+            .setDefaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.SKIP));
+    ;
+    
+    public void testShallowMapMerging() throws Exception
+    {
+        final String JSON = aposToQuotes("{'values':{'c':'y','d':null}}");
+        MergedMap v = MAPPER.readValue(JSON, MergedMap.class);
+        assertEquals(3, v.values.size());
+        assertEquals("y", v.values.get("c"));
+        assertEquals("x", v.values.get("a"));
+        assertNull(v.values.get("d"));
+
+        // but also, skip nulls
+        v = MAPPER_SKIP_NULLS.readValue(JSON, MergedMap.class);
+        assertEquals(2, v.values.size());
+        assertEquals("y", v.values.get("c"));
+        assertEquals("x", v.values.get("a"));
+    }
+
+    public void testShallowNonStringMerging() throws Exception
+    {
+        final String JSON = aposToQuotes("{'values':{'72':'b','666':null}}");
+        MergedIntMap v = MAPPER.readValue(JSON , MergedIntMap.class);
+        assertEquals(3, v.values.size());
+        assertEquals("a", v.values.get(Integer.valueOf(13)));
+        assertEquals("b", v.values.get(Integer.valueOf(72)));
+        assertNull(v.values.get(Integer.valueOf(666)));
+
+        v = MAPPER_SKIP_NULLS.readValue(JSON , MergedIntMap.class);
+        assertEquals(2, v.values.size());
+        assertEquals("a", v.values.get(Integer.valueOf(13)));
+        assertEquals("b", v.values.get(Integer.valueOf(72)));
+    }
+    
+    @SuppressWarnings("unchecked")
+    public void testDeeperMapMerging() throws Exception
+    {
+        // first, create base Map
+        MergedMap base = new MergedMap("name", "foobar");
+        Map<String,Object> props = new LinkedHashMap<>();
+        props.put("default", "yes");
+        props.put("x", "abc");
+        Map<String,Object> innerProps = new LinkedHashMap<>();
+        innerProps.put("z", Integer.valueOf(13));
+        props.put("extra", innerProps);
+        base.values.put("props", props);
+
+        // to be update
+        MergedMap v = MAPPER.readerForUpdating(base)
+                .readValue(aposToQuotes("{'values':{'props':{'x':'xyz','y' : '...','extra':{ 'ab' : true}}}}"));
+        assertEquals(2, v.values.size());
+        assertEquals("foobar", v.values.get("name"));
+        assertNotNull(v.values.get("props"));
+        props = (Map<String,Object>) v.values.get("props");
+        assertEquals(4, props.size());
+        assertEquals("yes", props.get("default"));
+        assertEquals("xyz", props.get("x"));
+        assertEquals("...", props.get("y"));
+        assertNotNull(props.get("extra"));
+        innerProps = (Map<String,Object>) props.get("extra");
+        assertEquals(2, innerProps.size());
+        assertEquals(Integer.valueOf(13), innerProps.get("z"));
+        assertEquals(Boolean.TRUE, innerProps.get("ab"));
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testMapMergingWithArray() throws Exception
+    {
+        // first, create base Map
+        MergedMap base = new MergedMap("name", "foobar");
+        Map<String,Object> props = new LinkedHashMap<>();
+        List<String> names = new ArrayList<>();
+        names.add("foo");
+        props.put("names", names);
+        base.values.put("props", props);
+        props.put("extra", "misc");
+
+        // to be update
+        MergedMap v = MAPPER.readerForUpdating(base)
+                .readValue(aposToQuotes("{'values':{'props':{'names': [ 'bar' ] }}}"));
+        assertEquals(2, v.values.size());
+        assertEquals("foobar", v.values.get("name"));
+        assertNotNull(v.values.get("props"));
+        props = (Map<String,Object>) v.values.get("props");
+        assertEquals(2, props.size());
+        assertEquals("misc", props.get("extra"));
+        assertNotNull(props.get("names"));
+        names = (List<String>) props.get("names");
+        assertEquals(2, names.size());
+        assertEquals("foo", names.get(0));
+        assertEquals("bar", names.get(1));
+    }
+
+    /*
+    /********************************************************
+    /* Forcing shallow merge of root Maps:
+    /********************************************************
+     */
+    
+    public void testDefaultDeepMapMerge() throws Exception
+    {
+        // First: deep merge should be enabled by default
+        HashMap<String,Object> input = new HashMap<>();
+        input.put("list", new ArrayList<>(Arrays.asList("a")));
+
+        Map<?,?> resultMap = MAPPER.readerForUpdating(input)
+                .readValue(aposToQuotes("{'list':['b']}"));
+
+        List<?> resultList = (List<?>) resultMap.get("list");
+        assertEquals(Arrays.asList("a", "b"), resultList);
+    }
+
+    public void testDisabledMergeViaGlobal() throws Exception
+    {
+        ObjectMapper mapper = newObjectMapper();
+        // disable merging, globally; does not affect main level
+        mapper.setDefaultMergeable(false);
+
+        HashMap<String,Object> input = new HashMap<>();
+        input.put("list", new ArrayList<>(Arrays.asList("a")));
+
+        Map<?,?> resultMap = mapper.readerForUpdating(input)
+                .readValue(aposToQuotes("{'list':['b']}"));
+
+        List<?> resultList = (List<?>) resultMap.get("list");
+
+        assertEquals(Arrays.asList("b"), resultList);
+    }
+
+    public void testDisabledMergeByType() throws Exception
+    {
+        ObjectMapper mapper = newObjectMapper();
+        // disable merging for "untyped", that is, `Object.class`
+        mapper.configOverride(Object.class)
+            .setMergeable(false);
+
+        HashMap<String,Object> input = new HashMap<>();
+        input.put("list", new ArrayList<>(Arrays.asList("a")));
+
+        Map<?,?> resultMap = mapper.readerForUpdating(input)
+                .readValue(aposToQuotes("{'list':['b']}"));
+        List<?> resultList = (List<?>) resultMap.get("list");
+        assertEquals(Arrays.asList("b"), resultList);
+
+        // and for extra points, disable by default but ENABLE for type,
+        // which should once again allow merging
+
+        mapper = newObjectMapper();
+        mapper.setDefaultMergeable(false);
+        mapper.configOverride(Object.class)
+            .setMergeable(true);
+
+        input = new HashMap<>();
+        input.put("list", new ArrayList<>(Arrays.asList("x")));
+
+        resultMap = mapper.readerForUpdating(input)
+                .readValue(aposToQuotes("{'list':['y']}"));
+        resultList = (List<?>) resultMap.get("list");
+        assertEquals(Arrays.asList("x", "y"), resultList);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/merge/MergeWithNullTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/merge/MergeWithNullTest.java
new file mode 100644
index 0000000..bc80ad3
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/merge/MergeWithNullTest.java
@@ -0,0 +1,133 @@
+package com.fasterxml.jackson.databind.deser.merge;
+
+import com.fasterxml.jackson.annotation.JsonMerge;
+import com.fasterxml.jackson.annotation.JsonSetter;
+import com.fasterxml.jackson.annotation.Nulls;
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.MapperFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class MergeWithNullTest extends BaseMapTest
+{
+    static class ConfigDefault {
+        @JsonMerge
+        public AB loc = new AB(1, 2);
+
+        protected ConfigDefault() { }
+        public ConfigDefault(int a, int b) {
+            loc = new AB(a, b);
+        }
+    }
+
+    static class ConfigSkipNull {
+        @JsonMerge
+        @JsonSetter(nulls=Nulls.SKIP)
+        public AB loc = new AB(1, 2);
+
+        protected ConfigSkipNull() { }
+        public ConfigSkipNull(int a, int b) {
+            loc = new AB(a, b);
+        }
+    }
+
+    static class ConfigAllowNullOverwrite {
+        @JsonMerge
+        @JsonSetter(nulls=Nulls.SET)
+        public AB loc = new AB(1, 2);
+
+        protected ConfigAllowNullOverwrite() { }
+        public ConfigAllowNullOverwrite(int a, int b) {
+            loc = new AB(a, b);
+        }
+    }
+    
+    // another variant where all we got is a getter
+    static class NoSetterConfig {
+        AB _value = new AB(2, 3);
+
+        @JsonMerge
+        public AB getValue() { return _value; }
+    }
+
+    static class AB {
+        public int a;
+        public int b;
+
+        protected AB() { }
+        public AB(int a0, int b0) {
+            a = a0;
+            b = b0;
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = newObjectMapper()
+            // 26-Oct-2016, tatu: Make sure we'll report merge problems by default
+            .disable(MapperFeature.IGNORE_MERGE_FOR_UNMERGEABLE)
+    ;
+
+    public void testBeanMergingWithNullDefault() throws Exception
+    {
+        // By default `null` should simply overwrite value
+        ConfigDefault config = MAPPER.readerForUpdating(new ConfigDefault(5, 7))
+                .readValue(aposToQuotes("{'loc':null}"));
+        assertNotNull(config);
+        assertNull(config.loc);
+
+        // but it should be possible to override setting to, say, skip
+
+        // First: via specific type override
+        // important! We'll specify for value type to be merged
+        ObjectMapper mapper = newObjectMapper();
+        mapper.configOverride(AB.class)
+            .setSetterInfo(JsonSetter.Value.forValueNulls(Nulls.SKIP));
+        config = mapper.readerForUpdating(new ConfigDefault(137, -3))
+                .readValue(aposToQuotes("{'loc':null}"));
+        assertNotNull(config.loc);
+        assertEquals(137, config.loc.a);
+        assertEquals(-3, config.loc.b);
+
+        // Second: by global defaults
+        mapper = newObjectMapper();
+        mapper.setDefaultSetterInfo(JsonSetter.Value.forValueNulls(Nulls.SKIP));
+        config = mapper.readerForUpdating(new ConfigDefault(12, 34))
+                .readValue(aposToQuotes("{'loc':null}"));
+        assertNotNull(config.loc);
+        assertEquals(12, config.loc.a);
+        assertEquals(34, config.loc.b);
+    }
+
+    public void testBeanMergingWithNullSkip() throws Exception
+    {
+        ConfigSkipNull config = MAPPER.readerForUpdating(new ConfigSkipNull(5, 7))
+                .readValue(aposToQuotes("{'loc':null}"));
+        assertNotNull(config);
+        assertNotNull(config.loc);
+        assertEquals(5, config.loc.a);
+        assertEquals(7, config.loc.b);
+    }
+
+    public void testBeanMergingWithNullSet() throws Exception
+    {
+        ConfigAllowNullOverwrite config = MAPPER.readerForUpdating(new ConfigAllowNullOverwrite(5, 7))
+                .readValue(aposToQuotes("{'loc':null}"));
+        assertNotNull(config);
+        assertNull(config.loc);
+    }
+    
+    public void testSetterlessMergingWithNull() throws Exception
+    {
+        NoSetterConfig input = new NoSetterConfig();
+        NoSetterConfig result = MAPPER.readerForUpdating(input)
+                .readValue(aposToQuotes("{'value':null}"));
+        assertNotNull(result.getValue());
+        assertEquals(2, result.getValue().a);
+        assertEquals(3, result.getValue().b);
+        assertSame(input, result);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/merge/NodeMergeTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/merge/NodeMergeTest.java
new file mode 100644
index 0000000..435ecdb
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/merge/NodeMergeTest.java
@@ -0,0 +1,117 @@
+package com.fasterxml.jackson.databind.deser.merge;
+
+import com.fasterxml.jackson.annotation.JsonMerge;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+
+public class NodeMergeTest extends BaseMapTest
+{
+    private final static ObjectMapper MAPPER = newObjectMapper()
+            // 26-Oct-2016, tatu: Make sure we'll report merge problems by default
+            .disable(MapperFeature.IGNORE_MERGE_FOR_UNMERGEABLE)
+    ;
+
+    static class ObjectNodeWrapper {
+        @JsonMerge
+        public ObjectNode props = MAPPER.createObjectNode();
+        {
+            props.put("default", "enabled");
+        }
+    }
+
+    static class ArrayNodeWrapper {
+        @JsonMerge
+        public ArrayNode list = MAPPER.createArrayNode();
+        {
+            list.add(123);
+        }
+    }
+
+    /*
+    /********************************************************
+    /* Test methods
+    /********************************************************
+     */
+
+    public void testObjectNodeUpdateValue() throws Exception
+    {
+        ObjectNode base = MAPPER.createObjectNode();
+        base.put("first", "foo");
+        assertSame(base,
+                MAPPER.readerForUpdating(base)
+                .readValue(aposToQuotes("{'second':'bar', 'third':5, 'fourth':true}")));
+        assertEquals(4, base.size());
+        assertEquals("bar", base.path("second").asText());
+        assertEquals("foo", base.path("first").asText());
+        assertEquals(5, base.path("third").asInt());
+        assertTrue(base.path("fourth").asBoolean());
+    }
+
+    public void testObjectNodeMerge() throws Exception
+    {
+        ObjectNodeWrapper w = MAPPER.readValue(aposToQuotes("{'props':{'stuff':'xyz'}}"),
+                ObjectNodeWrapper.class);
+        assertEquals(2, w.props.size());
+        assertEquals("enabled", w.props.path("default").asText());
+        assertEquals("xyz", w.props.path("stuff").asText());
+    }
+
+    public void testObjectDeepUpdate() throws Exception
+    {
+        ObjectNode base = MAPPER.createObjectNode();
+        ObjectNode props = base.putObject("props");
+        props.put("base", 123);
+        props.put("value", 456);
+        ArrayNode a = props.putArray("array");
+        a.add(true);
+        base.putNull("misc");
+        assertSame(base,
+                MAPPER.readerForUpdating(base)
+                .readValue(aposToQuotes(
+                        "{'props':{'value':true, 'extra':25.5, 'array' : [ 3 ]}}")));
+        assertEquals(2, base.size());
+        ObjectNode resultProps = (ObjectNode) base.get("props");
+        assertEquals(4, resultProps.size());
+        
+        assertEquals(123, resultProps.path("base").asInt());
+        assertTrue(resultProps.path("value").asBoolean());
+        assertEquals(25.5, resultProps.path("extra").asDouble());
+        JsonNode n = resultProps.get("array");
+        assertEquals(ArrayNode.class, n.getClass());
+        assertEquals(2, n.size());
+        assertEquals(3, n.get(1).asInt());
+    }
+
+    public void testArrayNodeUpdateValue() throws Exception
+    {
+        ArrayNode base = MAPPER.createArrayNode();
+        base.add("first");
+        assertSame(base,
+                MAPPER.readerForUpdating(base)
+                .readValue(aposToQuotes("['second',false,null]")));
+        assertEquals(4, base.size());
+        assertEquals("first", base.path(0).asText());
+        assertEquals("second", base.path(1).asText());
+        assertFalse(base.path(2).asBoolean());
+        assertTrue(base.path(3).isNull());
+    }
+
+    public void testArrayNodeMerge() throws Exception
+    {
+        ArrayNodeWrapper w = MAPPER.readValue(aposToQuotes("{'list':[456,true,{},  [], 'foo']}"),
+                ArrayNodeWrapper.class);
+        assertEquals(6, w.list.size());
+        assertEquals(123, w.list.get(0).asInt());
+        assertEquals(456, w.list.get(1).asInt());
+        assertTrue(w.list.get(2).asBoolean());
+        JsonNode n = w.list.get(3);
+        assertTrue(n.isObject());
+        assertEquals(0, n.size());
+        n = w.list.get(4);
+        assertTrue(n.isArray());
+        assertEquals(0, n.size());
+        assertEquals("foo", w.list.get(5).asText());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/merge/PropertyMergeTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/merge/PropertyMergeTest.java
new file mode 100644
index 0000000..826d465
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/merge/PropertyMergeTest.java
@@ -0,0 +1,229 @@
+package com.fasterxml.jackson.databind.deser.merge;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat.Shape;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.exc.MismatchedInputException;
+import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
+
+/**
+ * Tests to make sure that the new "merging" property of
+ * <code>JsonSetter</code> annotation works as expected.
+ * 
+ * @since 2.9
+ */
+@SuppressWarnings("serial")
+public class PropertyMergeTest extends BaseMapTest
+{
+    static class Config {
+        @JsonMerge
+        public AB loc = new AB(1, 2);
+
+        protected Config() { }
+        public Config(int a, int b) {
+            loc = new AB(a, b);
+        }
+    }
+
+    static class NonMergeConfig {
+        public AB loc = new AB(1, 2);
+    }
+
+    // another variant where all we got is a getter
+    static class NoSetterConfig {
+        AB _value = new AB(1, 2);
+ 
+        @JsonMerge
+        public AB getValue() { return _value; }
+    }
+
+    static class AB {
+        public int a;
+        public int b;
+
+        protected AB() { }
+        public AB(int a0, int b0) {
+            a = a0;
+            b = b0;
+        }
+    }
+
+    @JsonPropertyOrder(alphabetic=true)
+    @JsonFormat(shape=Shape.ARRAY)
+    static class ABAsArray {
+        public int a;
+        public int b;
+    }
+
+    // Custom type that would be deserializable by default
+    static class StringReference extends AtomicReference<String> {
+        public StringReference(String str) {
+            set(str);
+        }
+    }
+
+    static class MergedReference
+    {
+        @JsonMerge
+        public StringReference value = new StringReference("default");
+    }
+
+    static class MergedX<T>
+    {
+        @JsonMerge
+        public T value;
+
+        public MergedX(T v) { value = v; }
+        protected MergedX() { }
+    }
+    
+    // // // Classes with invalid merge definition(s)
+
+    static class CantMergeInts {
+        @JsonMerge
+        public int value;
+    }
+
+    /*
+    /********************************************************
+    /* Test methods, POJO merging
+    /********************************************************
+     */
+
+    private final ObjectMapper MAPPER = newObjectMapper()
+            // 26-Oct-2016, tatu: Make sure we'll report merge problems by default
+            .disable(MapperFeature.IGNORE_MERGE_FOR_UNMERGEABLE)
+    ;
+
+    public void testBeanMergingViaProp() throws Exception
+    {
+        Config config = MAPPER.readValue(aposToQuotes("{'loc':{'b':3}}"), Config.class);
+        assertEquals(1, config.loc.a);
+        assertEquals(3, config.loc.b);
+
+        config = MAPPER.readerForUpdating(new Config(5, 7))
+                .readValue(aposToQuotes("{'loc':{'b':2}}"));
+        assertEquals(5, config.loc.a);
+        assertEquals(2, config.loc.b);
+    }
+
+    public void testBeanMergingViaType() throws Exception
+    {
+        // by default, no merging
+        NonMergeConfig config = MAPPER.readValue(aposToQuotes("{'loc':{'a':3}}"), NonMergeConfig.class);
+        assertEquals(3, config.loc.a);
+        assertEquals(0, config.loc.b); // not passed, nor merge from original
+
+        // but with type-overrides
+        ObjectMapper mapper = newObjectMapper();
+        mapper.configOverride(AB.class).setMergeable(true);
+        config = mapper.readValue(aposToQuotes("{'loc':{'a':3}}"), NonMergeConfig.class);
+        assertEquals(3, config.loc.a);
+        assertEquals(2, config.loc.b); // original, merged
+    }
+
+    public void testBeanMergingViaGlobal() throws Exception
+    {
+        // but with type-overrides
+        ObjectMapper mapper = newObjectMapper()
+                .setDefaultMergeable(true);
+        NonMergeConfig config = mapper.readValue(aposToQuotes("{'loc':{'a':3}}"), NonMergeConfig.class);
+        assertEquals(3, config.loc.a);
+        assertEquals(2, config.loc.b); // original, merged
+
+        // also, test with bigger POJO type; just as smoke test
+        FiveMinuteUser user0 = new FiveMinuteUser("Bob", "Bush", true, FiveMinuteUser.Gender.MALE,
+                new byte[] { 1, 2, 3, 4, 5 });
+        FiveMinuteUser user = mapper.readerFor(FiveMinuteUser.class)
+                .withValueToUpdate(user0)
+                .readValue(aposToQuotes("{'name':{'last':'Brown'}}"));
+        assertEquals("Bob", user.getName().getFirst());
+        assertEquals("Brown", user.getName().getLast());
+    }
+
+    // should even work with no setter
+    public void testBeanMergingWithoutSetter() throws Exception
+    {
+        NoSetterConfig config = MAPPER.readValue(aposToQuotes("{'value':{'b':99}}"),
+                NoSetterConfig.class);
+        assertEquals(99, config._value.b);
+        assertEquals(1, config._value.a);
+    }
+
+    /*
+    /********************************************************
+    /* Test methods, as array
+    /********************************************************
+     */
+
+    public void testBeanAsArrayMerging() throws Exception
+    {
+        ABAsArray input = new ABAsArray();
+        input.a = 4;
+        input.b = 6;
+
+        assertSame(input, MAPPER.readerForUpdating(input)
+                .readValue("[1, 3]"));
+        assertEquals(1, input.a);
+        assertEquals(3, input.b);
+
+        // then with one too few
+        assertSame(input, MAPPER.readerForUpdating(input)
+                .readValue("[9]"));
+        assertEquals(9, input.a);
+        assertEquals(3, input.b);
+
+        // and finally with extra, failing
+        try {
+            MAPPER.readerForUpdating(input)
+                .readValue("[9, 8, 14]");
+            fail("Should not pass");
+        } catch (MismatchedInputException e) {
+            verifyException(e, "expected at most 2 properties");
+        }
+
+        try {
+            MAPPER.readerForUpdating(input)
+                .readValue("\"blob\"");
+            fail("Should not pass");
+        } catch (MismatchedInputException e) {
+            verifyException(e, "Cannot deserialize");
+            verifyException(e, "from non-Array representation");
+        }
+    }
+
+    /*
+    /********************************************************
+    /* Test methods, reference types
+    /********************************************************
+     */
+
+    public void testReferenceMerging() throws Exception
+    {
+        MergedReference result = MAPPER.readValue(aposToQuotes("{'value':'override'}"),
+                MergedReference.class);
+        assertEquals("override", result.value.get());
+    }
+
+    /*
+    /********************************************************
+    /* Test methods, failure checking
+    /********************************************************
+     */
+
+    public void testInvalidPropertyMerge() throws Exception
+    {
+        ObjectMapper mapper = newObjectMapper()
+                .disable(MapperFeature.IGNORE_MERGE_FOR_UNMERGEABLE);
+        
+        try {
+            mapper.readValue("{\"value\":3}", CantMergeInts.class);
+            fail("Should not pass");
+        } catch (InvalidDefinitionException e) {
+            verifyException(e, "cannot be merged");
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestValueUpdate.java b/src/test/java/com/fasterxml/jackson/databind/deser/merge/UpdateValueTest.java
similarity index 75%
rename from src/test/java/com/fasterxml/jackson/databind/creators/TestValueUpdate.java
rename to src/test/java/com/fasterxml/jackson/databind/deser/merge/UpdateValueTest.java
index 759f618..ce38f86 100644
--- a/src/test/java/com/fasterxml/jackson/databind/creators/TestValueUpdate.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/merge/UpdateValueTest.java
@@ -1,11 +1,11 @@
-package com.fasterxml.jackson.databind.creators;
+package com.fasterxml.jackson.databind.deser.merge;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
 
 import com.fasterxml.jackson.databind.*;
 
-public class TestValueUpdate extends BaseMapTest
+public class UpdateValueTest extends BaseMapTest
 {
     static class Bean
     {
@@ -36,7 +36,7 @@
         }
     }
 
-    private final ObjectMapper MAPPER = new ObjectMapper();
+    private final ObjectMapper MAPPER = newObjectMapper();
     
     // [databind#318] (and Scala module issue #83]
     public void testValueUpdateWithCreator() throws Exception
@@ -50,8 +50,11 @@
     public void testValueUpdateOther() throws Exception
     {
         Bean bean = new Bean("abc", "def");
-        ObjectReader r = MAPPER.reader().withValueToUpdate(bean);
+        ObjectReader r = MAPPER.readerFor(Bean.class).withValueToUpdate(bean);
         // but, changed our minds, no update
         r = r.withValueToUpdate(null);
+        // should be safe to read regardless
+        Bean result = r.readValue(aposToQuotes("{'a':'x'}"));
+        assertNotNull(result);
     }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/exc/BasicExceptionTest.java b/src/test/java/com/fasterxml/jackson/databind/exc/BasicExceptionTest.java
new file mode 100644
index 0000000..3848332
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/exc/BasicExceptionTest.java
@@ -0,0 +1,114 @@
+package com.fasterxml.jackson.databind.exc;
+
+import java.io.StringWriter;
+import java.util.Collection;
+import java.util.Collections;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+
+public class BasicExceptionTest extends BaseMapTest
+{
+    final ObjectMapper MAPPER = new ObjectMapper();
+    final JsonFactory JSON_F = MAPPER.getFactory();
+
+    public void testBadDefinition() throws Exception
+    {
+        JavaType t = TypeFactory.defaultInstance().constructType(String.class);
+        JsonParser p = JSON_F.createParser("[]");
+        InvalidDefinitionException e = new InvalidDefinitionException(p,
+               "Testing", t);
+        assertEquals("Testing", e.getOriginalMessage());
+        assertEquals(String.class, e.getType().getRawClass());
+        assertNull(e.getBeanDescription());
+        assertNull(e.getProperty());
+        assertSame(p, e.getProcessor());
+        p.close();
+
+        // and via factory method:
+        BeanDescription beanDef = MAPPER.getSerializationConfig().introspectClassAnnotations(getClass());
+        e = InvalidDefinitionException.from(p, "Testing",
+                beanDef, (BeanPropertyDefinition) null);
+        assertEquals(beanDef.getType(), e.getType());
+        assertNotNull(e);
+        
+        // and the other constructor too
+        JsonGenerator g = JSON_F.createGenerator(new StringWriter());
+        e = new InvalidDefinitionException(p,
+                "Testing", t);
+        assertEquals("Testing", e.getOriginalMessage());
+        assertEquals(String.class, e.getType().getRawClass());
+
+        // and factory
+        e = InvalidDefinitionException.from(g, "Testing",
+                beanDef, (BeanPropertyDefinition) null);
+        assertEquals(beanDef.getType(), e.getType());
+        assertNotNull(e);
+        
+        g.close();
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testInvalidFormat() throws Exception
+    {
+        // deprecated methods should still work:
+        InvalidFormatException e = new InvalidFormatException("Testing", Boolean.TRUE,
+                String.class);
+        assertSame(Boolean.TRUE, e.getValue());
+        assertNull(e.getProcessor());
+        assertNotNull(e);
+
+        e = new InvalidFormatException("Testing", JsonLocation.NA,
+                Boolean.TRUE, String.class);
+        assertSame(Boolean.TRUE, e.getValue());
+        assertNull(e.getProcessor());
+        assertNotNull(e);
+    }
+
+    public void testIgnoredProperty() throws Exception
+    {
+        // first just construct valid instance with some variations
+        JsonParser p = JSON_F.createParser("{ }");
+        IgnoredPropertyException e = IgnoredPropertyException.from(p,
+                this, // to get class from
+                "testProp", Collections.<Object>singletonList("x"));
+        assertNotNull(e);
+
+        e = IgnoredPropertyException.from(p,
+                getClass(),
+                "testProp", null);
+        assertNotNull(e);
+        assertNull(e.getKnownPropertyIds());
+        p.close();
+
+        // also, verify failure if null passed for "value"
+        try {
+            IgnoredPropertyException.from(p, null,
+                    "testProp", Collections.<Object>singletonList("x"));
+            fail("Should not pass");
+        } catch (NullPointerException e2) {
+        }
+    }
+
+    public void testUnrecognizedProperty() throws Exception
+    {
+        JsonParser p = JSON_F.createParser("{ }");
+        UnrecognizedPropertyException e = UnrecognizedPropertyException.from(p, this,
+                "testProp", Collections.<Object>singletonList("y"));
+        assertNotNull(e);
+        assertEquals(getClass(), e.getReferringClass());
+        Collection<Object> ids = e.getKnownPropertyIds();
+        assertNotNull(ids);
+        assertEquals(1, ids.size());
+        assertTrue(ids.contains("y"));
+
+        e = UnrecognizedPropertyException.from(p, getClass(),
+                "testProp", Collections.<Object>singletonList("y"));
+
+        assertEquals(getClass(), e.getReferringClass());
+        p.close();
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/exc/TestExceptionHandling.java b/src/test/java/com/fasterxml/jackson/databind/exc/DeserExceptionTypeTest.java
similarity index 61%
rename from src/test/java/com/fasterxml/jackson/databind/deser/exc/TestExceptionHandling.java
rename to src/test/java/com/fasterxml/jackson/databind/exc/DeserExceptionTypeTest.java
index 00c88ec..3b794c2 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/exc/TestExceptionHandling.java
+++ b/src/test/java/com/fasterxml/jackson/databind/exc/DeserExceptionTypeTest.java
@@ -1,34 +1,47 @@
-package com.fasterxml.jackson.databind.deser.exc;
+package com.fasterxml.jackson.databind.exc;
 
 import java.io.*;
 
 import com.fasterxml.jackson.core.*;
 import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.exc.MismatchedInputException;
 import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
 
 /**
  * Unit test for verifying that exceptions are properly handled (caught,
- * re-thrown or wrapped, depending)
- * with Object deserialization.
+ * re-thrown or wrapped, depending) with Object deserialization,
+ * including using concrete subtypes of {@link JsonMappingException}
+ * (or, for low-level parsing, {@link JsonParseException}).
  */
-public class TestExceptionHandling
+public class DeserExceptionTypeTest
     extends BaseMapTest
 {
     static class Bean {
         public String propX;
     }
 
+    // Class that has no applicable creators and thus cannot be instantiated;
+    // definition problem
+    static class NoCreatorsBean {
+        public int x;
+
+        // Constructor that is not detectable as Creator
+        public NoCreatorsBean(boolean foo, int foo2) { }
+    }
+    
     /*
     /**********************************************************
     /* Test methods
     /**********************************************************
      */
 
+    private final ObjectMapper MAPPER = new ObjectMapper();
+    
     public void testHandlingOfUnrecognized() throws Exception
     {
         UnrecognizedPropertyException exc = null;
         try {
-            new ObjectMapper().readValue("{\"bar\":3}", Bean.class);
+            MAPPER.readValue("{\"bar\":3}", Bean.class);
         } catch (UnrecognizedPropertyException e) {
             exc = e;
         }
@@ -48,12 +61,11 @@
      */
     public void testExceptionWithEmpty() throws Exception
     {
-        ObjectMapper mapper = new ObjectMapper();
         try {
-            Object result = mapper.readValue("    ", Object.class);
+            Object result = MAPPER.readValue("    ", Object.class);
             fail("Expected an exception, but got result value: "+result);
         } catch (Exception e) {
-            verifyException(e, JsonMappingException.class, "No content");
+            verifyException(e, MismatchedInputException.class, "No content");
         }
     }
 
@@ -62,12 +74,10 @@
         throws Exception
     {
         BrokenStringReader r = new BrokenStringReader("[ 1, ", "TEST");
-        JsonFactory f = new JsonFactory();
-        JsonParser jp = f.createParser(r);
-        ObjectMapper mapper = new ObjectMapper();
+        JsonParser p = MAPPER.getFactory().createParser(r);
         try {
             @SuppressWarnings("unused")
-            Object ob = mapper.readValue(jp, Object.class);
+            Object ob = MAPPER.readValue(p, Object.class);
             fail("Should have gotten an exception");
         } catch (IOException e) {
             /* For "bona fide" IO problems (due to low-level problem,
@@ -77,30 +87,37 @@
         }
     }
 
-    public void testExceptionWithEOF()
-        throws Exception
+    public void testExceptionWithEOF() throws Exception
     {
-        StringReader r = new StringReader("  3");
-        JsonFactory f = new JsonFactory();
-        JsonParser jp = f.createParser(r);
-        ObjectMapper mapper = new ObjectMapper();
+        JsonParser p = MAPPER.getFactory().createParser("  3");
 
-        Integer I = mapper.readValue(jp, Integer.class);
+        Integer I = MAPPER.readValue(p, Integer.class);
         assertEquals(3, I.intValue());
 
         // and then end-of-input...
         try {
-            I = mapper.readValue(jp, Integer.class);
+            I = MAPPER.readValue(p, Integer.class);
             fail("Should have gotten an exception");
         } catch (IOException e) {
-            verifyException(e, JsonMappingException.class, "No content");
+            verifyException(e, MismatchedInputException.class, "No content");
         }
         // also: should have no current token after end-of-input
-        JsonToken t = jp.getCurrentToken();
+        JsonToken t = p.getCurrentToken();
         if (t != null) {
             fail("Expected current token to be null after end-of-stream, was: "+t);
         }
-        jp.close();
+        p.close();
+    }
+
+    // [databind#1414]
+    public void testExceptionForNoCreators() throws Exception
+    {
+        try {
+            NoCreatorsBean b = MAPPER.readValue("{}", NoCreatorsBean.class);
+            fail("Should not succeed, got: "+b);
+        } catch (JsonMappingException e) {
+            verifyException(e, InvalidDefinitionException.class, "no Creators");
+        }
     }
 
     /*
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/exc/TestExceptionDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/exc/ExceptionDeserializationTest.java
similarity index 92%
rename from src/test/java/com/fasterxml/jackson/databind/deser/exc/TestExceptionDeserialization.java
rename to src/test/java/com/fasterxml/jackson/databind/exc/ExceptionDeserializationTest.java
index d89bfd6..536a5b4 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/exc/TestExceptionDeserialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/exc/ExceptionDeserializationTest.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.deser.exc;
+package com.fasterxml.jackson.databind.exc;
 
 import java.io.IOException;
 import java.util.*;
@@ -10,7 +10,7 @@
 /**
  * Unit tests for verifying that simple exceptions can be deserialized.
  */
-public class TestExceptionDeserialization
+public class ExceptionDeserializationTest
     extends BaseMapTest
 {
     @SuppressWarnings("serial")
@@ -170,4 +170,15 @@
         ), IOException.class);
         assertNotNull(exc);
     }
+
+    // [databind#1842]:
+    public void testNullAsMessage() throws IOException
+    {
+        Exception exc = MAPPER.readValue(aposToQuotes(
+                "{'message':null, 'localizedMessage':null }"
+        ), IOException.class);
+        assertNotNull(exc);
+        assertNull(exc.getMessage());
+        assertNull(exc.getLocalizedMessage());
+    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/exc/ExceptionPathTest.java b/src/test/java/com/fasterxml/jackson/databind/exc/ExceptionPathTest.java
similarity index 95%
rename from src/test/java/com/fasterxml/jackson/databind/deser/exc/ExceptionPathTest.java
rename to src/test/java/com/fasterxml/jackson/databind/exc/ExceptionPathTest.java
index c4a87bb..ae70ad3 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/exc/ExceptionPathTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/exc/ExceptionPathTest.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.deser.exc;
+package com.fasterxml.jackson.databind.exc;
 
 import com.fasterxml.jackson.annotation.*;
 
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/exc/TestExceptionSerialization.java b/src/test/java/com/fasterxml/jackson/databind/exc/ExceptionSerializationTest.java
similarity index 85%
rename from src/test/java/com/fasterxml/jackson/databind/deser/exc/TestExceptionSerialization.java
rename to src/test/java/com/fasterxml/jackson/databind/exc/ExceptionSerializationTest.java
index 07fc70a..fdee7db 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/exc/TestExceptionSerialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/exc/ExceptionSerializationTest.java
@@ -1,15 +1,16 @@
-package com.fasterxml.jackson.databind.deser.exc;
+package com.fasterxml.jackson.databind.exc;
 
 import java.io.IOException;
 import java.util.*;
 
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.core.JsonParser;
 import com.fasterxml.jackson.databind.*;
 
 /**
  * Unit tests for verifying that simple exceptions can be serialized.
  */
-public class TestExceptionSerialization
+public class ExceptionSerializationTest
     extends BaseMapTest
 {
     @SuppressWarnings("serial")
@@ -42,7 +43,7 @@
      */
 
     private final ObjectMapper MAPPER = new ObjectMapper();
-    
+
     public void testSimple() throws Exception
     {
         String TEST = "test exception";
@@ -66,6 +67,16 @@
         }
     }
 
+    // to double-check [databind#1413]
+    public void testSimpleOther() throws Exception
+    {
+        JsonParser p = MAPPER.getFactory().createParser("{ }");
+        InvalidFormatException exc = InvalidFormatException.from(p, "Test", getClass(), String.class);
+        String json = MAPPER.writeValueAsString(exc);
+        p.close();
+        assertNotNull(json);
+    }
+    
     // for [databind#877]
     @SuppressWarnings("unchecked")
     public void testIgnorals() throws Exception
@@ -109,15 +120,15 @@
             MAPPER.readValue( "{ \"val\": \"foo\" }", NoSerdeConstructor.class );
             fail("Should not pass");
         } catch (JsonMappingException e0) {
-            verifyException(e0, "no suitable constructor");
+            verifyException(e0, "cannot deserialize from Object");
             e = e0;
         }
         // but should be able to serialize new exception we got
         String json = MAPPER.writeValueAsString(e);
         JsonNode root = MAPPER.readTree(json);
         String msg = root.path("message").asText();
-        String MATCH = "no suitable constructor";
-        if (!msg.contains(MATCH)) {
+        String MATCH = "cannot construct instance";
+        if (!msg.toLowerCase().contains(MATCH)) {
             fail("Exception should contain '"+MATCH+"', does not: '"+msg+"'");
         }
     }
diff --git a/src/test/java/com/fasterxml/jackson/databind/exc/StackTraceElementTest.java b/src/test/java/com/fasterxml/jackson/databind/exc/StackTraceElementTest.java
new file mode 100644
index 0000000..2ed2786
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/exc/StackTraceElementTest.java
@@ -0,0 +1,39 @@
+package com.fasterxml.jackson.databind.exc;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.databind.*;
+
+// for [databind#1794]
+public class StackTraceElementTest extends BaseMapTest
+{
+    public static class ErrorObject {
+
+        public String throwable;
+        public String message;
+
+//        @JsonDeserialize(contentUsing = StackTraceElementDeserializer.class)
+        public StackTraceElement[] stackTrace;
+
+        ErrorObject() {}
+
+        public ErrorObject(Throwable throwable) {
+            this.throwable = throwable.getClass().getName();
+            message = throwable.getMessage();
+            stackTrace = throwable.getStackTrace();
+        }
+    }
+
+    // for [databind#1794] where extra `declaringClass` is serialized from private field.
+    public void testCustomStackTraceDeser() throws Exception
+    {
+        ObjectMapper mapper = newObjectMapper();
+        mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
+
+        String json = mapper
+                .writerWithDefaultPrettyPrinter()
+                .writeValueAsString(new ErrorObject(new Exception("exception message")));
+
+        ErrorObject result = mapper.readValue(json, ErrorObject.class);
+        assertNotNull(result);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/exc/TestExceptionHandlingWithDefaultDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/exc/TestExceptionHandlingWithDefaultDeserialization.java
similarity index 96%
rename from src/test/java/com/fasterxml/jackson/databind/deser/exc/TestExceptionHandlingWithDefaultDeserialization.java
rename to src/test/java/com/fasterxml/jackson/databind/exc/TestExceptionHandlingWithDefaultDeserialization.java
index 3888722..928b083 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/exc/TestExceptionHandlingWithDefaultDeserialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/exc/TestExceptionHandlingWithDefaultDeserialization.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.deser.exc;
+package com.fasterxml.jackson.databind.exc;
 
 import com.fasterxml.jackson.databind.BaseMapTest;
 import com.fasterxml.jackson.databind.JsonMappingException;
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/exc/TestExceptionHandlingWithJsonCreatorDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/exc/TestExceptionHandlingWithJsonCreatorDeserialization.java
similarity index 96%
rename from src/test/java/com/fasterxml/jackson/databind/deser/exc/TestExceptionHandlingWithJsonCreatorDeserialization.java
rename to src/test/java/com/fasterxml/jackson/databind/exc/TestExceptionHandlingWithJsonCreatorDeserialization.java
index 8065bc4..3797c80 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/exc/TestExceptionHandlingWithJsonCreatorDeserialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/exc/TestExceptionHandlingWithJsonCreatorDeserialization.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.deser.exc;
+package com.fasterxml.jackson.databind.exc;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/exc/TestExceptionsDuringWriting.java b/src/test/java/com/fasterxml/jackson/databind/exc/TestExceptionsDuringWriting.java
similarity index 98%
rename from src/test/java/com/fasterxml/jackson/databind/deser/exc/TestExceptionsDuringWriting.java
rename to src/test/java/com/fasterxml/jackson/databind/exc/TestExceptionsDuringWriting.java
index df51963..6c96343 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/exc/TestExceptionsDuringWriting.java
+++ b/src/test/java/com/fasterxml/jackson/databind/exc/TestExceptionsDuringWriting.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.deser.exc;
+package com.fasterxml.jackson.databind.exc;
 
 import java.io.*;
 import java.util.*;
diff --git a/src/test/java/com/fasterxml/jackson/databind/ext/TestJava7Types.java b/src/test/java/com/fasterxml/jackson/databind/ext/TestJava7Types.java
index 459978d..b18b8d1 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ext/TestJava7Types.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ext/TestJava7Types.java
@@ -1,12 +1,10 @@
 package com.fasterxml.jackson.databind.ext;
 
-import java.nio.file.FileSystem;
 import java.nio.file.Path;
+import java.nio.file.Paths;
 
 import com.fasterxml.jackson.databind.*;
-
-import com.google.common.jimfs.Configuration;
-import com.google.common.jimfs.Jimfs;
+import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
 
 /**
  * @since 2.7
@@ -17,17 +15,32 @@
     {
         ObjectMapper mapper = new ObjectMapper();
 
-        FileSystem fs = Jimfs.newFileSystem(Configuration.unix());
-        Path input = fs.getPath("/tmp", "foo.txt");
+        Path input = Paths.get("/tmp", "foo.txt");
 
         String json = mapper.writeValueAsString(input);
         assertNotNull(json);
-        
+
         Path p = mapper.readValue(json, Path.class);
         assertNotNull(p);
         
         assertEquals(input.toUri(), p.toUri());
         assertEquals(input, p);
-        fs.close();
+    }
+
+    // [databind#1688]:
+    public void testPolymorphicPath() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.enableDefaultTyping(DefaultTyping.NON_FINAL);
+        Path input = Paths.get("/tmp", "foo.txt");
+
+        String json = mapper.writeValueAsString(new Object[] { input });
+
+        Object[] obs = mapper.readValue(json, Object[].class);
+        assertEquals(1, obs.length);
+        Object ob = obs[0];
+        assertTrue(ob instanceof Path);
+
+        assertEquals(input.toString(), ob.toString());
     }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/filter/IgnorePropertiesDeser1575Test.java b/src/test/java/com/fasterxml/jackson/databind/filter/IgnorePropertiesDeser1575Test.java
deleted file mode 100644
index e35e09b..0000000
--- a/src/test/java/com/fasterxml/jackson/databind/filter/IgnorePropertiesDeser1575Test.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.fasterxml.jackson.databind.filter;
-
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-import com.fasterxml.jackson.databind.*;
-
-public class IgnorePropertiesDeser1575Test extends BaseMapTest
-{
-    static class Person {
-        public String name;
-
-        @JsonProperty("person_z") // renaming this to person_p works
-        @JsonIgnoreProperties({"person_z"}) // renaming this to person_p works
-//        public Set<Person> personZ;
-        public Person personZ;
-    }
-
-    public void testIgnorePropDeser1575() throws Exception
-    {
-        String st = aposToQuotes("{ 'name': 'admin',\n"
-//                + "    'person_z': [ { 'name': 'admin' } ]"
-              + "    'person_z': { 'name': 'admin' }"
-                + "}");
-
-        ObjectMapper mapper = new ObjectMapper();
-        Person result = mapper.readValue(st, Person.class);
-        assertEquals("admin", result.name);
-    }
-}
diff --git a/src/test/java/com/fasterxml/jackson/databind/filter/MapInclusionTest.java b/src/test/java/com/fasterxml/jackson/databind/filter/MapInclusionTest.java
deleted file mode 100644
index a5ccc57..0000000
--- a/src/test/java/com/fasterxml/jackson/databind/filter/MapInclusionTest.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package com.fasterxml.jackson.databind.filter;
-
-import java.io.IOException;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.databind.*;
-
-public class MapInclusionTest extends BaseMapTest
-{
-    static class NoEmptiesMapContainer {
-        @JsonInclude(value=JsonInclude.Include.NON_EMPTY,
-                content=JsonInclude.Include.NON_EMPTY)
-        public Map<String,String> stuff = new LinkedHashMap<String,String>();
-
-        public NoEmptiesMapContainer add(String key, String value) {
-            stuff.put(key, value);
-            return this;
-        }
-    }
-
-    /*
-    /**********************************************************
-    /* Test methods
-    /**********************************************************
-     */
-
-    final private ObjectMapper MAPPER = objectMapper();
-
-    // [databind#588]
-    public void testNonNullValueMapViaProp() throws IOException
-    {
-        String json = MAPPER.writeValueAsString(new NoEmptiesMapContainer()
-            .add("a", null)
-            .add("b", ""));
-        assertEquals(aposToQuotes("{}"), json);
-    }
-}
diff --git a/src/test/java/com/fasterxml/jackson/databind/format/BooleanFormatTest.java b/src/test/java/com/fasterxml/jackson/databind/format/BooleanFormatTest.java
new file mode 100644
index 0000000..5794cf2
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/format/BooleanFormatTest.java
@@ -0,0 +1,71 @@
+package com.fasterxml.jackson.databind.format;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+// [databind#1480]
+public class BooleanFormatTest extends BaseMapTest
+{
+    @JsonPropertyOrder({ "b1", "b2", "b3" })
+    static class BeanWithBoolean
+    {
+        @JsonFormat(shape=JsonFormat.Shape.NUMBER)
+        public boolean b1;
+
+        @JsonFormat(shape=JsonFormat.Shape.NUMBER)
+        public Boolean b2;
+
+        public boolean b3;
+
+        public BeanWithBoolean() { }
+        public BeanWithBoolean(boolean b1, Boolean b2, boolean b3) {
+            this.b1 = b1;
+            this.b2 = b2;
+            this.b3 = b3;
+        }
+    }
+
+    /**
+     * Simple wrapper around boolean types, usually to test value
+     * conversions or wrapping
+     */
+    protected static class BooleanWrapper {
+        public Boolean b;
+
+        public BooleanWrapper() { }
+        public BooleanWrapper(Boolean value) { b = value; }
+    }
+
+    static class AltBoolean extends BooleanWrapper
+    {
+        public AltBoolean() { }
+        public AltBoolean(Boolean b) { super(b); }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    private final static ObjectMapper MAPPER = newObjectMapper();
+
+    public void testShapeViaDefaults() throws Exception
+    {
+        assertEquals(aposToQuotes("{'b':true}"),
+                MAPPER.writeValueAsString(new BooleanWrapper(true)));
+        ObjectMapper m = newObjectMapper();
+        m.configOverride(Boolean.class)
+            .setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.NUMBER));
+        assertEquals(aposToQuotes("{'b':1}"),
+                m.writeValueAsString(new BooleanWrapper(true)));
+    }
+
+    public void testShapeOnProperty() throws Exception
+    {
+        assertEquals(aposToQuotes("{'b1':1,'b2':0,'b3':true}"),
+                MAPPER.writeValueAsString(new BeanWithBoolean(true, false, true)));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/TestFormatForCollections.java b/src/test/java/com/fasterxml/jackson/databind/format/CollectionFormatShapeTest.java
similarity index 85%
rename from src/test/java/com/fasterxml/jackson/databind/struct/TestFormatForCollections.java
rename to src/test/java/com/fasterxml/jackson/databind/format/CollectionFormatShapeTest.java
index bd308b3..6eea8fe 100644
--- a/src/test/java/com/fasterxml/jackson/databind/struct/TestFormatForCollections.java
+++ b/src/test/java/com/fasterxml/jackson/databind/format/CollectionFormatShapeTest.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.struct;
+package com.fasterxml.jackson.databind.format;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -8,7 +8,7 @@
 
 import com.fasterxml.jackson.databind.*;
 
-public class TestFormatForCollections extends BaseMapTest
+public class CollectionFormatShapeTest extends BaseMapTest
 {
     // [databind#40]: Allow serialization 'as POJO' (resulting in JSON Object) 
     @JsonPropertyOrder({ "size", "value" })
@@ -40,11 +40,9 @@
     /**********************************************************
      */
 
-    private final static ObjectMapper MAPPER = new ObjectMapper();    
+    private final static ObjectMapper MAPPER = newObjectMapper();    
 
-
-    // [Issue#40]
-    public void testListAsObject() throws Exception
+    public void testListAsObjectRoundtrip() throws Exception
     {
         // First, serialize a "POJO-List"
         CollectionAsPOJO list = new CollectionAsPOJO();
diff --git a/src/test/java/com/fasterxml/jackson/databind/format/DateFormatTest.java b/src/test/java/com/fasterxml/jackson/databind/format/DateFormatTest.java
index 19b97f7..f94bd02 100644
--- a/src/test/java/com/fasterxml/jackson/databind/format/DateFormatTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/format/DateFormatTest.java
@@ -18,7 +18,7 @@
 
     public void testTypeDefaults() throws Exception
     {
-        ObjectMapper mapper = new ObjectMapper();
+        ObjectMapper mapper = newObjectMapper();
         mapper.configOverride(Date.class)
             .setFormat(JsonFormat.Value.forPattern("yyyy.dd.MM"));
         // First serialize, should result in this (in UTC):
diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/EnumFormatShapeTest.java b/src/test/java/com/fasterxml/jackson/databind/format/EnumFormatShapeTest.java
similarity index 96%
rename from src/test/java/com/fasterxml/jackson/databind/struct/EnumFormatShapeTest.java
rename to src/test/java/com/fasterxml/jackson/databind/format/EnumFormatShapeTest.java
index 8e7f13b..4e11a8e 100644
--- a/src/test/java/com/fasterxml/jackson/databind/struct/EnumFormatShapeTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/format/EnumFormatShapeTest.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.struct;
+package com.fasterxml.jackson.databind.format;
 
 import com.fasterxml.jackson.annotation.*;
 import com.fasterxml.jackson.annotation.JsonFormat.Shape;
@@ -76,7 +76,7 @@
     /**********************************************************
      */
 
-    private final ObjectMapper MAPPER = new ObjectMapper();
+    private final ObjectMapper MAPPER = newObjectMapper();
 
     // Tests for JsonFormat.shape
 
diff --git a/src/test/java/com/fasterxml/jackson/databind/format/MapEntryFormatTest.java b/src/test/java/com/fasterxml/jackson/databind/format/MapEntryFormatTest.java
new file mode 100644
index 0000000..ce875a5
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/format/MapEntryFormatTest.java
@@ -0,0 +1,179 @@
+package com.fasterxml.jackson.databind.format;
+
+import java.util.*;
+import java.util.concurrent.atomic.AtomicReference;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.*;
+
+public class MapEntryFormatTest extends BaseMapTest
+{
+    static class BeanWithMapEntry {
+        // would work with any other shape than OBJECT, or without annotation:
+        @JsonFormat(shape=JsonFormat.Shape.NATURAL)
+        public Map.Entry<String,String> entry;
+
+        protected BeanWithMapEntry() { }
+        public BeanWithMapEntry(String key, String value) {
+            Map<String,String> map = new HashMap<>();
+            map.put(key, value);
+            entry = map.entrySet().iterator().next();
+        }
+    }
+
+    @JsonFormat(shape=JsonFormat.Shape.OBJECT)
+    static class MapEntryAsObject implements Map.Entry<String,String> {
+        protected String key, value;
+
+        protected MapEntryAsObject() { }
+        public MapEntryAsObject(String k, String v) {
+            key = k;
+            value = v;
+        }
+        
+        @Override
+        public String getKey() {
+            return key;
+        }
+
+        @Override
+        public String getValue() {
+            return value;
+        }
+
+        @Override
+        public String setValue(String v) {
+            value = v;
+            return v; // wrong, whatever
+        }
+    }
+
+    static class EntryWithNullWrapper {
+        @JsonInclude(value=JsonInclude.Include.NON_EMPTY,
+                content=JsonInclude.Include.NON_NULL)
+        public Map.Entry<String,String> entry;
+
+        public EntryWithNullWrapper(String key, String value) {
+            HashMap<String,String> map = new HashMap<>();
+            map.put(key, value);
+            entry = map.entrySet().iterator().next();
+        }
+    }
+
+    static class EntryWithDefaultWrapper {
+        @JsonInclude(value=JsonInclude.Include.NON_EMPTY,
+                content=JsonInclude.Include.NON_DEFAULT)
+        public Map.Entry<String,String> entry;
+
+        public EntryWithDefaultWrapper(String key, String value) {
+            HashMap<String,String> map = new HashMap<>();
+            map.put(key, value);
+            entry = map.entrySet().iterator().next();
+        }
+    }
+
+    static class EntryWithNonAbsentWrapper {
+        @JsonInclude(value=JsonInclude.Include.NON_EMPTY,
+                content=JsonInclude.Include.NON_ABSENT)
+        public Map.Entry<String,AtomicReference<String>> entry;
+
+        public EntryWithNonAbsentWrapper(String key, String value) {
+            HashMap<String,AtomicReference<String>> map = new HashMap<>();
+            map.put(key, new AtomicReference<String>(value));
+            entry = map.entrySet().iterator().next();
+        }
+    }
+    
+    static class EmptyEntryWrapper {
+        @JsonInclude(value=JsonInclude.Include.NON_EMPTY,
+                content=JsonInclude.Include.NON_EMPTY)
+        public Map.Entry<String,String> entry;
+
+        public EmptyEntryWrapper(String key, String value) {
+            HashMap<String,String> map = new HashMap<>();
+            map.put(key, value);
+            entry = map.entrySet().iterator().next();
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Test methods, basic
+    /**********************************************************
+     */
+    
+    private final ObjectMapper MAPPER = newObjectMapper();
+
+    public void testInclusion() throws Exception
+    {
+        assertEquals(aposToQuotes("{'entry':{'a':'b'}}"),
+                MAPPER.writeValueAsString(new EmptyEntryWrapper("a", "b")));
+        assertEquals(aposToQuotes("{'entry':{'a':'b'}}"),
+                MAPPER.writeValueAsString(new EntryWithDefaultWrapper("a", "b")));
+        assertEquals(aposToQuotes("{'entry':{'a':'b'}}"),
+                MAPPER.writeValueAsString(new EntryWithNullWrapper("a", "b")));
+
+        assertEquals(aposToQuotes("{}"),
+                MAPPER.writeValueAsString(new EmptyEntryWrapper("a", "")));
+        assertEquals(aposToQuotes("{}"),
+                MAPPER.writeValueAsString(new EntryWithDefaultWrapper("a", "")));
+        assertEquals(aposToQuotes("{'entry':{'a':''}}"),
+                MAPPER.writeValueAsString(new EntryWithNullWrapper("a", "")));
+        assertEquals(aposToQuotes("{}"),
+                MAPPER.writeValueAsString(new EntryWithNullWrapper("a", null)));
+    }
+
+    public void testInclusionWithReference() throws Exception
+    {
+        assertEquals(aposToQuotes("{'entry':{'a':'b'}}"),
+                MAPPER.writeValueAsString(new EntryWithNonAbsentWrapper("a", "b")));
+        // empty String not excluded since reference is not absent, just points to empty
+        // (so would need 3rd level inclusion definition)
+        assertEquals(aposToQuotes("{'entry':{'a':''}}"),
+                MAPPER.writeValueAsString(new EntryWithNonAbsentWrapper("a", "")));
+        assertEquals(aposToQuotes("{}"),
+                MAPPER.writeValueAsString(new EntryWithNonAbsentWrapper("a", null)));
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods, as-Object (Shape)
+    /**********************************************************
+     */
+
+    public void testAsNaturalRoundtrip() throws Exception
+    {
+        BeanWithMapEntry input = new BeanWithMapEntry("foo" ,"bar");
+        String json = MAPPER.writeValueAsString(input);
+        assertEquals(aposToQuotes("{'entry':{'foo':'bar'}}"), json);
+        BeanWithMapEntry result = MAPPER.readValue(json, BeanWithMapEntry.class);
+        assertEquals("foo", result.entry.getKey());
+        assertEquals("bar", result.entry.getValue());
+    }
+    // should work via class annotation
+    public void testAsObjectRoundtrip() throws Exception
+    {
+        MapEntryAsObject input = new MapEntryAsObject("foo" ,"bar");
+        String json = MAPPER.writeValueAsString(input);
+        assertEquals(aposToQuotes("{'key':'foo','value':'bar'}"), json);
+
+        // 16-Oct-2016, tatu: Happens to work by default because it's NOT basic
+        //   `Map.Entry` but subtype.
+        
+        MapEntryAsObject result = MAPPER.readValue(json, MapEntryAsObject.class);
+        assertEquals("foo", result.getKey());
+        assertEquals("bar", result.getValue());
+    }
+
+    // [databind#1895]
+    public void testDefaultShapeOverride() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.configOverride(Map.Entry.class)
+            .setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.OBJECT));
+        Map.Entry<String,String> input = new BeanWithMapEntry("foo", "bar").entry;
+        assertEquals(aposToQuotes("{'key':'foo','value':'bar'}"),
+                mapper.writeValueAsString(input));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/format/MapFormatShapeTest.java b/src/test/java/com/fasterxml/jackson/databind/format/MapFormatShapeTest.java
new file mode 100644
index 0000000..a475d40
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/format/MapFormatShapeTest.java
@@ -0,0 +1,205 @@
+package com.fasterxml.jackson.databind.format;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import com.fasterxml.jackson.databind.*;
+
+@SuppressWarnings("serial")
+public class MapFormatShapeTest extends BaseMapTest
+{
+    @JsonPropertyOrder({ "extra" })
+    static class Map476Base extends LinkedHashMap<String,Integer> {
+        public int extra = 13;
+    }
+
+    @JsonFormat(shape=JsonFormat.Shape.OBJECT)
+    static class Map476AsPOJO extends Map476Base { }
+
+    @JsonPropertyOrder({ "a", "b", "c" })
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    static class Bean476Container
+    {
+        public Map476AsPOJO a;
+        public Map476Base b;
+        @JsonFormat(shape=JsonFormat.Shape.OBJECT)
+        public Map476Base c;
+
+        public Bean476Container(int forA, int forB, int forC) {
+            if (forA != 0) {
+                a = new Map476AsPOJO();
+                a.put("value", forA);
+            }
+            if (forB != 0) {
+                b = new Map476Base();
+                b.put("value", forB);
+            }
+            if (forC != 0) {
+                c = new Map476Base();
+                c.put("value", forC);
+            }
+        }
+    }
+
+    static class Bean476Override
+    {
+        @JsonFormat(shape=JsonFormat.Shape.NATURAL)
+        public Map476AsPOJO stuff;
+
+        public Bean476Override(int value) {
+            stuff = new Map476AsPOJO();
+            stuff.put("value", value);
+        }
+    }
+
+    // from [databind#1540]
+    @JsonFormat(shape = JsonFormat.Shape.OBJECT)
+    @JsonPropertyOrder({ "property", "map" })
+    static class Map1540Implementation implements Map<Integer, Integer> {
+        public int property;
+        public Map<Integer, Integer> map = new HashMap<>();
+ 
+        public Map<Integer, Integer> getMap() {
+            return map;
+       }
+
+       public void setMap(Map<Integer, Integer> map) {
+            this.map = map;
+       }        
+
+       @Override
+       public Integer put(Integer key, Integer value) {
+            return map.put(key, value);
+       }
+
+        @Override
+        public int size() {
+            return map.size();
+        }
+
+        @JsonIgnore
+        @Override
+        public boolean isEmpty() {
+            return map.isEmpty();
+        }
+
+        @Override
+        public boolean containsKey(Object key) {
+            return map.containsKey(key);
+        }
+
+        @Override
+        public boolean containsValue(Object value) {
+            return map.containsValue(value);
+        }
+    
+        @Override
+        public Integer get(Object key) {
+            return map.get(key);
+        }
+    
+        @Override
+        public Integer remove(Object key) {
+            return map.remove(key);
+        }
+    
+        @Override
+        public void putAll(Map<? extends Integer, ? extends Integer> m) {
+            map.putAll(m);
+        }
+
+        @Override
+        public void clear() {
+            map.clear();
+        }
+
+        @Override
+        public Set<Integer> keySet() {
+            return map.keySet();
+        }
+
+        @Override
+        public Collection<Integer> values() {
+            return map.values();
+        }
+    
+        @Override
+        public Set<java.util.Map.Entry<Integer, Integer>> entrySet() {
+            return map.entrySet();
+        }
+    }
+
+    
+    /*
+    /**********************************************************
+    /* Test methods, serialization
+    /**********************************************************
+     */
+
+    final private ObjectMapper MAPPER = objectMapper();
+
+    // for [databind#476]: Maps as POJOs
+    public void testSerializeAsPOJOViaClass() throws Exception
+    {
+        String result = MAPPER.writeValueAsString(new Bean476Container(1,2,0));
+        assertEquals(aposToQuotes("{'a':{'extra':13,'empty':false},'b':{'value':2}}"),
+                result);
+    }
+
+    // Can't yet use per-property overrides at all, see [databind#1419]
+    
+    /*
+    public void testSerializeAsPOJOViaProperty() throws Exception
+    {
+        String result = MAPPER.writeValueAsString(new Bean476Container(1,0,3));
+        assertEquals(aposToQuotes("{'a':{'extra':13,'empty':false},'c':{'empty':false,'value':3}}"),
+                result);
+    }
+
+    public void testSerializeNaturalViaOverride() throws Exception
+    {
+        String result = MAPPER.writeValueAsString(new Bean476Override(123));
+        assertEquals(aposToQuotes("{'stuff':{'value':123}}"),
+                result);
+    }
+    */
+
+    /*
+    /**********************************************************
+    /* Test methods, deserialization/roundtrip
+    /**********************************************************
+     */
+
+    // [databind#1540]
+    public void testRoundTrip() throws Exception
+    {
+        Map1540Implementation input = new Map1540Implementation();
+        input.property = 55;
+        input.put(12, 45);
+        input.put(6, 88);
+
+        String json = MAPPER.writeValueAsString(input);
+
+        assertEquals(aposToQuotes("{'property':55,'map':{'6':88,'12':45}}"), json);
+
+        Map1540Implementation result = MAPPER.readValue(json, Map1540Implementation.class);
+        assertEquals(result.property, input.property);
+        assertEquals(input.getMap(), input.getMap());
+   }
+    
+    // [databind#1554]
+    public void testDeserializeAsPOJOViaClass() throws Exception
+    {
+        Map476AsPOJO result = MAPPER.readValue(aposToQuotes("{'extra':42}"),
+                Map476AsPOJO.class);
+        assertEquals(0, result.size());
+        assertEquals(42, result.extra);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/interop/IllegalTypesCheckTest.java b/src/test/java/com/fasterxml/jackson/databind/interop/IllegalTypesCheckTest.java
index f7d1517..5ccb0fb 100644
--- a/src/test/java/com/fasterxml/jackson/databind/interop/IllegalTypesCheckTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/interop/IllegalTypesCheckTest.java
@@ -8,10 +8,9 @@
 
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
 import com.fasterxml.jackson.databind.*;
-import com.mchange.v2.c3p0.jacksontest.ComboPooledDataSource;
+import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
 
-import java.util.ArrayList;
-import java.util.List;
+import com.mchange.v2.c3p0.jacksontest.ComboPooledDataSource;
 
 /**
  * Test case(s) to guard against handling of types that are illegal to handle
@@ -138,8 +137,7 @@
 
     protected void _verifySecurityException(Throwable t, String clsName) throws Exception
     {
-        // 17-Aug-2017, tatu: Expected type more granular in 2.9 (over 2.8)
-        _verifyException(t, JsonMappingException.class,
+        _verifyException(t, InvalidDefinitionException.class,
             "Illegal type",
             "to deserialize",
             "prevented for security reasons");
diff --git a/src/test/java/com/fasterxml/jackson/databind/interop/TestFormatDetection.java b/src/test/java/com/fasterxml/jackson/databind/interop/TestFormatDetection.java
index 18345a6..3d1da2f 100644
--- a/src/test/java/com/fasterxml/jackson/databind/interop/TestFormatDetection.java
+++ b/src/test/java/com/fasterxml/jackson/databind/interop/TestFormatDetection.java
@@ -23,7 +23,7 @@
     /* Test methods
     /**********************************************************
      */
-    
+
     public void testSimpleWithJSON() throws Exception
     {
         ObjectReader detecting = READER.forType(POJO.class);
@@ -33,6 +33,45 @@
         assertEquals(1, pojo.x);
     }
 
+    public void testSequenceWithJSON() throws Exception
+    {
+        ObjectReader detecting = READER.forType(POJO.class);
+        detecting = detecting.withFormatDetection(detecting);
+        MappingIterator<POJO> it = detecting.
+                readValues(utf8Bytes(aposToQuotes("{'x':1}\n{'x':2,'y':5}")));
+
+        assertTrue(it.hasNextValue());
+        POJO pojo = it.nextValue();
+        assertEquals(1, pojo.x);
+
+        assertTrue(it.hasNextValue());
+        pojo = it.nextValue();
+        assertEquals(2, pojo.x);
+        assertEquals(5, pojo.y);
+        
+        assertFalse(it.hasNextValue());
+        it.close();
+
+        // And again with nodes
+        ObjectReader r2 = READER.forType(JsonNode.class);
+        r2 = r2.withFormatDetection(r2);
+        MappingIterator<JsonNode> nodes = r2.
+                readValues(utf8Bytes(aposToQuotes("{'x':1}\n{'x':2,'y':5}")));
+
+        assertTrue(nodes.hasNextValue());
+        JsonNode n = nodes.nextValue();
+        assertEquals(1, n.size());
+
+        assertTrue(nodes.hasNextValue());
+        n = nodes.nextValue();
+        assertEquals(2, n.size());
+        assertEquals(2, n.path("x").asInt());
+        assertEquals(5, n.path("y").asInt());
+
+        assertFalse(nodes.hasNextValue());
+        nodes.close();
+    }
+
     public void testInvalid() throws Exception
     {
         ObjectReader detecting = READER.forType(POJO.class);
@@ -41,7 +80,7 @@
             detecting.readValue(utf8Bytes("<POJO><x>1</x></POJO>"));
             fail("Should have failed");
         } catch (JsonProcessingException e) {
-            verifyException(e, "Can not detect format from input");
+            verifyException(e, "Cannot detect format from input");
         }
     }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/AutoDetect1947Test.java b/src/test/java/com/fasterxml/jackson/databind/introspect/AutoDetect1947Test.java
new file mode 100644
index 0000000..c048917
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/introspect/AutoDetect1947Test.java
@@ -0,0 +1,45 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import com.fasterxml.jackson.databind.*;
+
+// Test(s) for [databind#1947], regression for 2.9
+public class AutoDetect1947Test extends BaseMapTest
+{
+    static class Entity1947 {
+        public int shouldBeDetected;
+        public String shouldNotBeDetected;
+
+        @JsonProperty
+        public int getShouldBeDetected() {
+            return shouldBeDetected;
+        }
+
+        public void setShouldBeDetected(int shouldBeDetected) {
+            this.shouldBeDetected = shouldBeDetected;
+        }
+
+        public String getShouldNotBeDetected() {
+            return shouldNotBeDetected;
+        }
+
+        public void setShouldNotBeDetected(String shouldNotBeDetected) {
+            this.shouldNotBeDetected = shouldNotBeDetected;
+        }
+    }
+    public void testDisablingAll() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper()
+                .disable(MapperFeature.AUTO_DETECT_SETTERS)
+                .disable(MapperFeature.AUTO_DETECT_FIELDS)
+                .disable(MapperFeature.AUTO_DETECT_GETTERS)
+                .disable(MapperFeature.AUTO_DETECT_CREATORS)
+                .disable(MapperFeature.AUTO_DETECT_IS_GETTERS);
+        String json = mapper.writeValueAsString(new Entity1947());
+        JsonNode n = mapper.readTree(json);
+        assertEquals(1, n.size());
+        assertTrue(n.has("shouldBeDetected"));
+        assertFalse(n.has("shouldNotBeDetected"));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/failing/CustomAnnotationIntrospector1756Test.java b/src/test/java/com/fasterxml/jackson/databind/introspect/CustomAnnotationIntrospector1756Test.java
similarity index 97%
rename from src/test/java/com/fasterxml/jackson/failing/CustomAnnotationIntrospector1756Test.java
rename to src/test/java/com/fasterxml/jackson/databind/introspect/CustomAnnotationIntrospector1756Test.java
index 80e6438..62d6207 100644
--- a/src/test/java/com/fasterxml/jackson/failing/CustomAnnotationIntrospector1756Test.java
+++ b/src/test/java/com/fasterxml/jackson/databind/introspect/CustomAnnotationIntrospector1756Test.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.failing;
+package com.fasterxml.jackson.databind.introspect;
 
 import java.io.IOException;
 import java.lang.annotation.*;
@@ -7,7 +7,6 @@
 import com.fasterxml.jackson.core.JsonParser;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.introspect.*;
 import com.fasterxml.jackson.databind.module.SimpleModule;
 
 @SuppressWarnings("serial")
@@ -88,6 +87,7 @@
         return null;
       }
 
+      @SuppressWarnings("deprecation")
       @Override
       public boolean hasCreatorAnnotation(Annotated a) {
         final AnnotatedConstructor ctor = (AnnotatedConstructor) a;
diff --git a/src/test/java/com/fasterxml/jackson/failing/IgnoredCreatorProperty1572Test.java b/src/test/java/com/fasterxml/jackson/databind/introspect/IgnoredCreatorProperty1572Test.java
similarity index 95%
rename from src/test/java/com/fasterxml/jackson/failing/IgnoredCreatorProperty1572Test.java
rename to src/test/java/com/fasterxml/jackson/databind/introspect/IgnoredCreatorProperty1572Test.java
index e3ed8b9..a6eeca8 100644
--- a/src/test/java/com/fasterxml/jackson/failing/IgnoredCreatorProperty1572Test.java
+++ b/src/test/java/com/fasterxml/jackson/databind/introspect/IgnoredCreatorProperty1572Test.java
@@ -1,10 +1,9 @@
-package com.fasterxml.jackson.failing;
+package com.fasterxml.jackson.databind.introspect;
 
 import com.fasterxml.jackson.annotation.*;
 
 import com.fasterxml.jackson.databind.BaseMapTest;
 import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.introspect.*;
 
 public class IgnoredCreatorProperty1572Test extends BaseMapTest
 {
diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/IgnoredFieldPresentInCreatorProperty2001Test.java b/src/test/java/com/fasterxml/jackson/databind/introspect/IgnoredFieldPresentInCreatorProperty2001Test.java
new file mode 100644
index 0000000..a551c0a
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/introspect/IgnoredFieldPresentInCreatorProperty2001Test.java
@@ -0,0 +1,27 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import java.beans.ConstructorProperties;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+
+public class IgnoredFieldPresentInCreatorProperty2001Test extends BaseMapTest
+{
+   static public class Foo {
+        @JsonIgnore
+        public String query;
+
+        // 01-May-2018, tatu: Important! Without this there is no problem
+        @ConstructorProperties("rawQuery")
+        @JsonCreator
+        public Foo(@JsonProperty("query") String rawQuery) {
+        query = rawQuery;
+      }
+    }
+
+    public void testIgnoredFieldPresentInPropertyCreator() throws Exception {
+        Foo deserialized = newObjectMapper().readValue("{\"query\": \"bar\"}", Foo.class);
+        assertEquals("bar", deserialized.query);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/IntrospectorPairTest.java b/src/test/java/com/fasterxml/jackson/databind/introspect/IntrospectorPairTest.java
index 73a3a42..a862713 100644
--- a/src/test/java/com/fasterxml/jackson/databind/introspect/IntrospectorPairTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/introspect/IntrospectorPairTest.java
@@ -1,8 +1,21 @@
 package com.fasterxml.jackson.databind.introspect;
 
+import java.lang.annotation.Annotation;
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import com.fasterxml.jackson.annotation.JsonInclude;
+
 import com.fasterxml.jackson.core.Version;
+
 import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.cfg.MapperConfig;
+import com.fasterxml.jackson.databind.deser.std.StringDeserializer;
+import com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer;
+import com.fasterxml.jackson.databind.jsontype.NamedType;
+import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
+import com.fasterxml.jackson.databind.ser.std.StringSerializer;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
 
 // started with [databind#1025] in mind
 @SuppressWarnings("serial")
@@ -36,9 +49,440 @@
         }
     }
 
+    static class IntrospectorWithHandlers extends AnnotationIntrospector {
+        final Object _deserializer;
+        final Object _serializer;
+
+        public IntrospectorWithHandlers(Object deser, Object ser) {
+            _deserializer = deser;
+            _serializer = ser;
+        }
+
+        @Override
+        public Version version() {
+            return Version.unknownVersion();
+        }
+
+        @Override
+        public Object findDeserializer(Annotated am) {
+            return _deserializer;
+        }
+
+        @Override
+        public Object findSerializer(Annotated am) {
+            return _serializer;
+        }
+    }
+
+    static class IntrospectorWithMap extends AnnotationIntrospector
+    {
+        private final Map<String, Object> values = new HashMap<>();
+
+        private Version version = Version.unknownVersion();
+
+        public IntrospectorWithMap add(String key, Object value) {
+            values.put(key, value);
+            return this;
+        }
+
+        public IntrospectorWithMap version(Version v) {
+            version = v;
+            return this;
+        }
+
+        @Override
+        public Version version() {
+            return version;
+        }
+
+        @Override
+        public JsonInclude.Value findPropertyInclusion(Annotated a) {
+            return JsonInclude.Value.empty()
+                    .withContentInclusion(JsonInclude.Include.NON_EMPTY)
+                    .withValueInclusion(JsonInclude.Include.USE_DEFAULTS);
+        }
+
+        @Override
+        public boolean isAnnotationBundle(Annotation ann) {
+            return _boolean("isAnnotationBundle");
+        }
+
+        /*
+        /******************************************************
+        /* General class annotations
+        /******************************************************
+         */        
+
+        @Override
+        public PropertyName findRootName(AnnotatedClass ac) {
+            return (PropertyName) values.get("findRootName");
+        }
+
+        @Override
+        public JsonIgnoreProperties.Value findPropertyIgnorals(Annotated a) {
+            return (JsonIgnoreProperties.Value) values.get("findPropertyIgnorals");
+        }
+
+        @Override
+        public Boolean isIgnorableType(AnnotatedClass ac) {
+            return (Boolean) values.get("isIgnorableType");
+        }
+
+        @Override
+        public Object findFilterId(Annotated ann) {
+            return (Object) values.get("findFilterId");
+        }
+        
+        @Override
+        public Object findNamingStrategy(AnnotatedClass ac) {
+            return (Object) values.get("findNamingStrategy");
+        }
+
+        @Override
+        public String findClassDescription(AnnotatedClass ac) {
+            return (String) values.get("findClassDescription");
+        }
+
+        /*
+        /******************************************************
+        /* Property auto-detection
+        /******************************************************
+        */
+
+        @Override
+        public VisibilityChecker<?> findAutoDetectVisibility(AnnotatedClass ac,
+            VisibilityChecker<?> checker)
+        {
+            VisibilityChecker<?> vc = (VisibilityChecker<?>) values.get("findAutoDetectVisibility");
+            // not really good but:
+            return (vc == null) ? checker : vc;
+        }
+
+        /*
+        /******************************************************
+        /* Type handling
+        /******************************************************
+         */
+
+        @Override
+        public TypeResolverBuilder<?> findTypeResolver(MapperConfig<?> config,
+                AnnotatedClass ac, JavaType baseType)
+        {
+            return (TypeResolverBuilder<?>) values.get("findTypeResolver");
+        }
+
+        @Override
+        public TypeResolverBuilder<?> findPropertyTypeResolver(MapperConfig<?> config,
+                AnnotatedMember am, JavaType baseType)
+        {
+            return (TypeResolverBuilder<?>) values.get("findPropertyTypeResolver");
+        }
+
+        @Override
+        public TypeResolverBuilder<?> findPropertyContentTypeResolver(MapperConfig<?> config,
+                AnnotatedMember am, JavaType baseType)
+        {
+            return (TypeResolverBuilder<?>) values.get("findPropertyContentTypeResolver");
+        }
+        
+        @SuppressWarnings("unchecked")
+        @Override
+        public List<NamedType> findSubtypes(Annotated a)
+        {
+            return (List<NamedType>) values.get("findSubtypes");
+        }
+
+        @Override
+        public String findTypeName(AnnotatedClass ac) {
+            return (String) values.get("findTypeName");
+        }
+
+        /*
+        /******************************************************
+        /* Deserialization introspection
+        /******************************************************
+         */
+
+        @Override
+        public Boolean hasAnySetter(Annotated a) {
+            return (Boolean) values.get("hasAnySetter");
+        }
+
+        /*
+        /******************************************************
+        /* Helper methods
+        /******************************************************
+         */        
+
+        private boolean _boolean(String key) {
+            Object ob = values.get(key);
+            return Boolean.TRUE.equals(ob);
+        }
+    }
+
     /*
     /**********************************************************
-    /* Test methods
+    /* Test methods, misc
+    /**********************************************************
+     */
+
+    private final AnnotationIntrospector NO_ANNOTATIONS = AnnotationIntrospector.nopInstance();
+
+    public void testVersion() throws Exception
+    {
+        Version v = new Version(1, 2, 3, null,
+                "com.fasterxml", "IntrospectorPairTest");
+        IntrospectorWithMap withVersion = new IntrospectorWithMap()
+                .version(v);
+        assertEquals(v,
+                new AnnotationIntrospectorPair(withVersion, NO_ANNOTATIONS).version());
+        IntrospectorWithMap noVersion = new IntrospectorWithMap();
+        assertEquals(Version.unknownVersion(),
+                new AnnotationIntrospectorPair(noVersion, withVersion).version());
+    }
+
+    public void testAccess() throws Exception
+    {
+        IntrospectorWithMap intr1 = new IntrospectorWithMap();
+        AnnotationIntrospectorPair pair = new AnnotationIntrospectorPair(intr1,
+                NO_ANNOTATIONS);
+        Collection<AnnotationIntrospector> intrs = pair.allIntrospectors();
+        assertEquals(2, intrs.size());
+        Iterator<AnnotationIntrospector> it = intrs.iterator();
+        assertSame(intr1, it.next());
+        assertSame(NO_ANNOTATIONS, it.next());
+    }
+
+    public void testAnnotationBundle() throws Exception
+    {
+        IntrospectorWithMap isBundle = new IntrospectorWithMap()
+                .add("isAnnotationBundle", true);
+        assertTrue(new AnnotationIntrospectorPair(NO_ANNOTATIONS, isBundle)
+                .isAnnotationBundle(null));
+        assertTrue(new AnnotationIntrospectorPair(isBundle, NO_ANNOTATIONS)
+                .isAnnotationBundle(null));
+        assertFalse(new AnnotationIntrospectorPair(NO_ANNOTATIONS, NO_ANNOTATIONS)
+                .isAnnotationBundle(null));
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods, general class annotations
+    /**********************************************************
+     */
+
+    public void testFindRootName() throws Exception
+    {
+        PropertyName name = new PropertyName("test");
+        IntrospectorWithMap intr = new IntrospectorWithMap()
+                .add("findRootName", name);
+        assertNull(new AnnotationIntrospectorPair(NO_ANNOTATIONS, NO_ANNOTATIONS).findRootName(null));
+        assertEquals(name, new AnnotationIntrospectorPair(NO_ANNOTATIONS, intr).findRootName(null));
+        assertEquals(name, new AnnotationIntrospectorPair(intr, NO_ANNOTATIONS).findRootName(null));
+    }
+
+    public void testPropertyIgnorals() throws Exception
+    {
+        JsonIgnoreProperties.Value incl = JsonIgnoreProperties.Value.forIgnoredProperties("foo");
+        IntrospectorWithMap intr = new IntrospectorWithMap()
+                .add("findPropertyIgnorals", incl);
+        IntrospectorWithMap intrEmpty = new IntrospectorWithMap()
+                .add("findPropertyIgnorals", JsonIgnoreProperties.Value.empty());
+        assertEquals(JsonIgnoreProperties.Value.empty(),
+                new AnnotationIntrospectorPair(intrEmpty, intrEmpty).findPropertyIgnorals(null));
+        // should actually verify inclusion combining, but there are separate tests for that
+        assertEquals(incl, new AnnotationIntrospectorPair(intrEmpty, intr).findPropertyIgnorals(null));
+        assertEquals(incl, new AnnotationIntrospectorPair(intr, intrEmpty).findPropertyIgnorals(null));
+    }
+
+    public void testIsIgnorableType() throws Exception
+    {
+        IntrospectorWithMap intr1 = new IntrospectorWithMap()
+                .add("isIgnorableType", Boolean.TRUE);
+        IntrospectorWithMap intr2 = new IntrospectorWithMap()
+                .add("isIgnorableType", Boolean.FALSE);
+        assertNull(new AnnotationIntrospectorPair(NO_ANNOTATIONS, NO_ANNOTATIONS).isIgnorableType(null));
+        assertEquals(Boolean.TRUE, new AnnotationIntrospectorPair(intr1, intr2).isIgnorableType(null));
+        assertEquals(Boolean.FALSE, new AnnotationIntrospectorPair(intr2, intr1).isIgnorableType(null));
+    }
+
+    public void testFindFilterId() throws Exception
+    {
+        IntrospectorWithMap intr1 = new IntrospectorWithMap()
+                .add("findFilterId", "a");
+        IntrospectorWithMap intr2 = new IntrospectorWithMap()
+                .add("findFilterId", "b");
+        assertNull(new AnnotationIntrospectorPair(NO_ANNOTATIONS, NO_ANNOTATIONS).findFilterId(null));
+        assertEquals("a", new AnnotationIntrospectorPair(intr1, intr2).findFilterId(null));
+        assertEquals("b", new AnnotationIntrospectorPair(intr2, intr1).findFilterId(null));
+    }
+
+    public void testFindNamingStrategy() throws Exception
+    {
+        // shouldn't be bogus Classes for real use, but works here
+        IntrospectorWithMap intr1 = new IntrospectorWithMap()
+                .add("findNamingStrategy", Integer.class);
+        IntrospectorWithMap intr2 = new IntrospectorWithMap()
+                .add("findNamingStrategy", String.class);
+        assertNull(new AnnotationIntrospectorPair(NO_ANNOTATIONS, NO_ANNOTATIONS).findNamingStrategy(null));
+        assertEquals(Integer.class,
+                new AnnotationIntrospectorPair(intr1, intr2).findNamingStrategy(null));
+        assertEquals(String.class,
+                new AnnotationIntrospectorPair(intr2, intr1).findNamingStrategy(null));
+    }
+
+    public void testFindClassDescription() throws Exception
+    {
+        IntrospectorWithMap intr1 = new IntrospectorWithMap()
+                .add("findClassDescription", "Desc1");
+        IntrospectorWithMap intr2 = new IntrospectorWithMap()
+                .add("findClassDescription", "Desc2");
+        assertNull(new AnnotationIntrospectorPair(NO_ANNOTATIONS, NO_ANNOTATIONS).findClassDescription(null));
+        assertEquals("Desc1",
+                new AnnotationIntrospectorPair(intr1, intr2).findClassDescription(null));
+        assertEquals("Desc2",
+                new AnnotationIntrospectorPair(intr2, intr1).findClassDescription(null));
+    }
+
+    // // // 3 deprecated methods, skip
+
+    /*
+    /**********************************************************
+    /* Test methods, ser/deser
+    /**********************************************************
+     */
+
+    public void testFindSerializer() throws Exception
+    {
+        final JsonSerializer<?> serString = new StringSerializer();
+        final JsonSerializer<?> serToString = ToStringSerializer.instance;
+
+        AnnotationIntrospector intr1 = new IntrospectorWithHandlers(null, serString);
+        AnnotationIntrospector intr2 = new IntrospectorWithHandlers(null, serToString);
+        AnnotationIntrospector nop = AnnotationIntrospector.nopInstance();
+        AnnotationIntrospector nop2 = new IntrospectorWithHandlers(null, JsonSerializer.None.class);
+
+        assertSame(serString,
+                new AnnotationIntrospectorPair(intr1, intr2).findSerializer(null));
+        assertSame(serToString,
+                new AnnotationIntrospectorPair(intr2, intr1).findSerializer(null));
+
+        // also: no-op instance should not block real one, regardless
+        assertSame(serString,
+                new AnnotationIntrospectorPair(nop, intr1).findSerializer(null));
+        assertSame(serString,
+                new AnnotationIntrospectorPair(nop2, intr1).findSerializer(null));
+
+        // nor should no-op result in non-null result
+        assertNull(new AnnotationIntrospectorPair(nop, nop2).findSerializer(null));
+        assertNull(new AnnotationIntrospectorPair(nop2, nop).findSerializer(null));
+    }
+
+    public void testFindDeserializer() throws Exception
+    {
+        final JsonDeserializer<?> deserString = StringDeserializer.instance;
+        final JsonDeserializer<?> deserObject = UntypedObjectDeserializer.Vanilla.std;
+
+        AnnotationIntrospector intr1 = new IntrospectorWithHandlers(deserString, null);
+        AnnotationIntrospector intr2 = new IntrospectorWithHandlers(deserObject, null);
+        AnnotationIntrospector nop = AnnotationIntrospector.nopInstance();
+        AnnotationIntrospector nop2 = new IntrospectorWithHandlers(JsonDeserializer.None.class, null);
+
+        assertSame(deserString,
+                new AnnotationIntrospectorPair(intr1, intr2).findDeserializer(null));
+        assertSame(deserObject,
+                new AnnotationIntrospectorPair(intr2, intr1).findDeserializer(null));
+        // also: no-op instance should not block real one, regardless
+        assertSame(deserString,
+                new AnnotationIntrospectorPair(nop, intr1).findDeserializer(null));
+        assertSame(deserString,
+                new AnnotationIntrospectorPair(nop2, intr1).findDeserializer(null));
+
+        // nor should no-op result in non-null result
+        assertNull(new AnnotationIntrospectorPair(nop, nop2).findDeserializer(null));
+        assertNull(new AnnotationIntrospectorPair(nop2, nop).findDeserializer(null));
+    }
+
+    /*
+    /******************************************************
+    /* Property auto-detection
+    /******************************************************
+     */
+
+    public void testFindAutoDetectVisibility() throws Exception
+    {
+        VisibilityChecker<?> vc = VisibilityChecker.Std.defaultInstance();
+        IntrospectorWithMap intr1 = new IntrospectorWithMap()
+                .add("findAutoDetectVisibility", vc);
+        assertNull(new AnnotationIntrospectorPair(NO_ANNOTATIONS, NO_ANNOTATIONS)
+                .findAutoDetectVisibility(null, null));
+        assertSame(vc, new AnnotationIntrospectorPair(intr1, NO_ANNOTATIONS)
+                .findAutoDetectVisibility(null, null));
+        assertSame(vc, new AnnotationIntrospectorPair(NO_ANNOTATIONS, intr1)
+                .findAutoDetectVisibility(null, null));
+    }
+
+    /*
+    /******************************************************
+    /* Type handling
+    /******************************************************
+     */
+
+    public void testFindTypeResolver() throws Exception
+    {
+        /*
+        TypeResolverBuilder<?> findTypeResolver(MapperConfig<?> config,
+            AnnotatedClass ac, JavaType baseType)
+        return (TypeResolverBuilder<?>) values.get("findTypeResolver");
+        */
+    }
+    public void testFindPropertyTypeResolver() {
+    }
+
+    public void testFindPropertyContentTypeResolver() {
+    }
+
+    public void testFindSubtypes() {
+    }
+
+    public void testFindTypeName() {
+        IntrospectorWithMap intr1 = new IntrospectorWithMap()
+                .add("findTypeName", "type1");
+        IntrospectorWithMap intr2 = new IntrospectorWithMap()
+                .add("findTypeName", "type2");
+        assertNull(new AnnotationIntrospectorPair(NO_ANNOTATIONS, NO_ANNOTATIONS).findTypeName(null));
+        assertEquals("type1",
+                new AnnotationIntrospectorPair(intr1, intr2).findTypeName(null));
+        assertEquals("type2",
+                new AnnotationIntrospectorPair(intr2, intr1).findTypeName(null));
+    }
+
+    /*
+    /******************************************************
+    /* Deserialization introspection
+    /******************************************************
+     */
+
+    // for [databind#1672]
+    public void testHasAnySetter() {
+        IntrospectorWithMap intr1 = new IntrospectorWithMap()
+                .add("hasAnySetter", Boolean.TRUE);
+        IntrospectorWithMap intr2 = new IntrospectorWithMap()
+                .add("hasAnySetter", Boolean.FALSE);
+        assertNull(new AnnotationIntrospectorPair(NO_ANNOTATIONS, NO_ANNOTATIONS).hasAnySetter(null));
+        assertEquals(Boolean.TRUE,
+                new AnnotationIntrospectorPair(intr1, intr2).hasAnySetter(null));
+        assertEquals(Boolean.TRUE,
+                new AnnotationIntrospectorPair(NO_ANNOTATIONS, intr1).hasAnySetter(null));
+        assertEquals(Boolean.FALSE,
+                new AnnotationIntrospectorPair(intr2, intr1).hasAnySetter(null));
+        assertEquals(Boolean.FALSE,
+                new AnnotationIntrospectorPair(NO_ANNOTATIONS, intr2).hasAnySetter(null));
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods, others
     /**********************************************************
      */
 
@@ -47,7 +491,7 @@
 
     private final AnnotationIntrospectorPair introPair21
         = new AnnotationIntrospectorPair(new Introspector2(), new Introspector1());
-    
+
     // for [databind#1025]
     public void testInclusionMerging() throws Exception
     {
diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/TestPOJOPropertiesCollector.java b/src/test/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollectorTest.java
similarity index 97%
rename from src/test/java/com/fasterxml/jackson/databind/introspect/TestPOJOPropertiesCollector.java
rename to src/test/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollectorTest.java
index e621ed4..378b135 100644
--- a/src/test/java/com/fasterxml/jackson/databind/introspect/TestPOJOPropertiesCollector.java
+++ b/src/test/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollectorTest.java
@@ -11,7 +11,7 @@
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
 
-public class TestPOJOPropertiesCollector
+public class POJOPropertiesCollectorTest
     extends BaseMapTest
 {
     static class Simple {
@@ -438,13 +438,16 @@
 
     public void testJackson744() throws Exception
     {
-        BeanDescription beanDesc = MAPPER.getDeserializationConfig().introspect(MAPPER.constructType(Issue744Bean.class));
+        BeanDescription beanDesc = MAPPER.getDeserializationConfig().introspect
+                (MAPPER.constructType(Issue744Bean.class));
         assertNotNull(beanDesc);
-        AnnotatedMethod setter = beanDesc.findAnySetter();
+        AnnotatedMember setter = beanDesc.findAnySetterAccessor();
         assertNotNull(setter);
+        assertEquals("addAdditionalProperty", setter.getName());
+        assertTrue(setter instanceof AnnotatedMethod);
     }
 
-    // [#269]: Support new @JsonPropertyDescription
+    // [databind#269]: Support new @JsonPropertyDescription
     public void testPropertyDesc() throws Exception
     {
         // start via deser
@@ -455,7 +458,7 @@
         _verifyProperty(beanDesc, true, false, "13");
     }
 
-    // [#438]: Support @JsonProperty.index
+    // [databind#438]: Support @JsonProperty.index
     public void testPropertyIndex() throws Exception
     {
         BeanDescription beanDesc = MAPPER.getDeserializationConfig().introspect(MAPPER.constructType(PropDescBean.class));
@@ -523,7 +526,6 @@
             }
         }
     }
-
     /*
     /**********************************************************
     /* Helper methods
diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/PropertyMetadataTest.java b/src/test/java/com/fasterxml/jackson/databind/introspect/PropertyMetadataTest.java
new file mode 100644
index 0000000..84dcc54
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/introspect/PropertyMetadataTest.java
@@ -0,0 +1,82 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import com.fasterxml.jackson.annotation.Nulls;
+import com.fasterxml.jackson.databind.*;
+
+public class PropertyMetadataTest extends BaseMapTest
+{
+    public void testPropertyName()
+    {
+        PropertyName name = PropertyName.NO_NAME;
+        
+        assertFalse(name.hasSimpleName());
+        assertFalse(name.hasNamespace());
+        assertSame(name, name.internSimpleName());
+        assertSame(name, name.withSimpleName(null));
+        assertSame(name, name.withSimpleName(""));
+        assertSame(name, name.withNamespace(null));
+        assertEquals("", name.toString());
+        assertTrue(name.isEmpty());
+        assertFalse(name.hasSimpleName("foo"));
+        // just to trigger it, ensure to exception
+        name.hashCode();
+
+        PropertyName newName = name.withNamespace("");
+        assertNotSame(name, newName);
+        assertTrue(name.equals(name));
+        assertFalse(name.equals(newName));
+        assertFalse(newName.equals(name));
+
+        name = name.withSimpleName("foo");
+        assertEquals("foo", name.toString());
+        assertTrue(name.hasSimpleName("foo"));
+        assertFalse(name.isEmpty());
+        newName = name.withNamespace("ns");
+        assertEquals("{ns}foo", newName.toString());
+        assertFalse(newName.equals(name));
+        assertFalse(name.equals(newName));
+
+        // just to trigger it, ensure to exception
+        name.hashCode();
+    }
+
+    public void testPropertyMetadata()
+    {
+        PropertyMetadata md = PropertyMetadata.STD_OPTIONAL;
+        assertNull(md.getValueNulls());
+        assertNull(md.getContentNulls());
+        assertNull(md.getDefaultValue());
+        assertEquals(Boolean.FALSE, md.getRequired());
+
+        md = md.withNulls(Nulls.AS_EMPTY,
+                Nulls.FAIL);
+        assertEquals(Nulls.AS_EMPTY, md.getValueNulls());
+        assertEquals(Nulls.FAIL, md.getContentNulls());
+
+        assertFalse(md.hasDefaultValue());
+        assertSame(md, md.withDefaultValue(null));
+        assertSame(md, md.withDefaultValue(""));
+        md = md.withDefaultValue("foo");
+        assertEquals("foo", md.getDefaultValue());
+        assertTrue(md.hasDefaultValue());
+        assertSame(md, md.withDefaultValue("foo"));
+        md = md.withDefaultValue(null);
+        assertFalse(md.hasDefaultValue());
+        assertNull(md.getDefaultValue());
+
+        md = md.withRequired(null);
+        assertNull(md.getRequired());
+        assertFalse(md.isRequired());
+        md = md.withRequired(Boolean.TRUE);
+        assertTrue(md.isRequired());
+        assertSame(md, md.withRequired(Boolean.TRUE));
+        md = md.withRequired(null);
+        assertNull(md.getRequired());
+        assertFalse(md.isRequired());
+ 
+        assertFalse(md.hasIndex());
+        md = md.withIndex(Integer.valueOf(3));
+        assertTrue(md.hasIndex());
+        assertEquals(Integer.valueOf(3), md.getIndex());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/TestAnnotionBundles.java b/src/test/java/com/fasterxml/jackson/databind/introspect/TestAnnotationBundles.java
similarity index 82%
rename from src/test/java/com/fasterxml/jackson/databind/introspect/TestAnnotionBundles.java
rename to src/test/java/com/fasterxml/jackson/databind/introspect/TestAnnotationBundles.java
index 761f0bc..e9a0714 100644
--- a/src/test/java/com/fasterxml/jackson/databind/introspect/TestAnnotionBundles.java
+++ b/src/test/java/com/fasterxml/jackson/databind/introspect/TestAnnotationBundles.java
@@ -6,6 +6,7 @@
 import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
 import com.fasterxml.jackson.annotation.JsonAutoDetect;
 import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
+import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.databind.ObjectMapper;
@@ -13,7 +14,7 @@
 
 /* Tests mostly for [JACKSON-754]: ability to create "annotation bundles"
  */
-public class TestAnnotionBundles extends com.fasterxml.jackson.databind.BaseMapTest
+public class TestAnnotationBundles extends com.fasterxml.jackson.databind.BaseMapTest
 {
     @Retention(RetentionPolicy.RUNTIME)
     @JacksonAnnotationsInside
@@ -71,6 +72,18 @@
         @HolderA public int unimportant = 42;
     }
 
+    static class RecursiveHolder2 {
+        @HolderA public int getValue() { return 28; }
+    }
+
+    static class RecursiveHolder3 {
+        public int x;
+
+        @JsonCreator
+        @HolderA
+        public RecursiveHolder3(int x) { this.x = x; }
+    }
+    
     @JsonProperty
     @JacksonAnnotationsInside
     @Retention(RetentionPolicy.RUNTIME)
@@ -110,11 +123,20 @@
         assertEquals("{\"important\":42}", MAPPER.writeValueAsString(new InformingHolder()));
     }
 
-    public void testRecursiveBundles() throws Exception
-    {
+    public void testRecursiveBundlesField() throws Exception {
         assertEquals("{\"unimportant\":42}", MAPPER.writeValueAsString(new RecursiveHolder()));
     }
 
+    public void testRecursiveBundlesMethod() throws Exception {
+        assertEquals("{\"value\":28}", MAPPER.writeValueAsString(new RecursiveHolder2()));
+    }
+
+    public void testRecursiveBundlesConstructor() throws Exception {
+        RecursiveHolder3 result = MAPPER.readValue("17", RecursiveHolder3.class);
+        assertNotNull(result);
+        assertEquals(17, result.x);
+    }
+    
     public void testBundledIgnore() throws Exception
     {
         assertEquals("{\"foobar\":13}", MAPPER.writeValueAsString(new Bean()));
diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/TestAutoDetect.java b/src/test/java/com/fasterxml/jackson/databind/introspect/TestAutoDetect.java
index c5817bc..9ab933d 100644
--- a/src/test/java/com/fasterxml/jackson/databind/introspect/TestAutoDetect.java
+++ b/src/test/java/com/fasterxml/jackson/databind/introspect/TestAutoDetect.java
@@ -1,7 +1,7 @@
 package com.fasterxml.jackson.databind.introspect;
 
 import com.fasterxml.jackson.annotation.*;
-
+import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
 import com.fasterxml.jackson.core.*;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.introspect.VisibilityChecker;
@@ -16,13 +16,33 @@
 
         private PrivateBean(String a) { this.a = a; }
     }
-    
+
+    // test for [databind#1347], config overrides for visibility
+    @JsonPropertyOrder(alphabetic=true)
+    static class Feature1347SerBean {
+        public int field = 2;
+
+        public int getValue() { return 3; }
+    }
+
+    // let's promote use of fields; but not block setters yet
+    @JsonAutoDetect(fieldVisibility=Visibility.NON_PRIVATE)
+    static class Feature1347DeserBean {
+        int value;
+
+        public void setValue(int x) {
+            throw new IllegalArgumentException("Should NOT get called");
+        }
+    }
+
     /*
     /********************************************************
     /* Unit tests
     /********************************************************
      */
-    
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+
     public void testPrivateCtor() throws Exception
     {
         // first, default settings, with which construction works ok
@@ -43,4 +63,42 @@
         }
     }
 
+    // [databind#1347]
+    public void testVisibilityConfigOverridesForSer() throws Exception
+    {
+        // first, by default, both field/method should be visible
+        final Feature1347SerBean input = new Feature1347SerBean();
+        assertEquals(aposToQuotes("{'field':2,'value':3}"),
+                MAPPER.writeValueAsString(input));
+
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.configOverride(Feature1347SerBean.class)
+            .setVisibility(JsonAutoDetect.Value.construct(PropertyAccessor.GETTER,
+                            Visibility.NONE));
+        assertEquals(aposToQuotes("{'field':2}"),
+                mapper.writeValueAsString(input));
+    }
+
+    // [databind#1347]
+    public void testVisibilityConfigOverridesForDeser() throws Exception
+    {
+        final String JSON = aposToQuotes("{'value':3}");
+
+        // by default, should throw exception
+        try {
+            /*Feature1347DeserBean bean =*/
+            MAPPER.readValue(JSON, Feature1347DeserBean.class);
+            fail("Should not pass");
+        } catch (JsonMappingException e) {
+            verifyException(e, "Should NOT get called");
+        }
+
+        // but when instructed to ignore setter, should work
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.configOverride(Feature1347DeserBean.class)
+            .setVisibility(JsonAutoDetect.Value.construct(PropertyAccessor.SETTER,
+                        Visibility.NONE));
+        Feature1347DeserBean result = mapper.readValue(JSON, Feature1347DeserBean.class);
+        assertEquals(3, result.value);
+    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/TestJacksonAnnotationIntrospector.java b/src/test/java/com/fasterxml/jackson/databind/introspect/TestJacksonAnnotationIntrospector.java
index d506473..ef9adde 100644
--- a/src/test/java/com/fasterxml/jackson/databind/introspect/TestJacksonAnnotationIntrospector.java
+++ b/src/test/java/com/fasterxml/jackson/databind/introspect/TestJacksonAnnotationIntrospector.java
@@ -185,7 +185,8 @@
     {
         ObjectMapper mapper = new ObjectMapper();
         JacksonAnnotationIntrospector ai = new JacksonAnnotationIntrospector();
-        AnnotatedClass ac = AnnotatedClass.constructWithoutSuperTypes(TypeResolverBean.class, mapper.getSerializationConfig());
+        AnnotatedClass ac = AnnotatedClassResolver.resolveWithoutSuperTypes(mapper.getSerializationConfig(),
+                TypeResolverBean.class);
         JavaType baseType = TypeFactory.defaultInstance().constructType(TypeResolverBean.class);
         TypeResolverBuilder<?> rb = ai.findTypeResolver(mapper.getDeserializationConfig(), ac, baseType);
         assertNotNull(rb);
diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/TestScalaLikeImplicitProperties.java b/src/test/java/com/fasterxml/jackson/databind/introspect/TestScalaLikeImplicitProperties.java
index b6121bb..397db2f 100644
--- a/src/test/java/com/fasterxml/jackson/databind/introspect/TestScalaLikeImplicitProperties.java
+++ b/src/test/java/com/fasterxml/jackson/databind/introspect/TestScalaLikeImplicitProperties.java
@@ -1,6 +1,8 @@
 package com.fasterxml.jackson.databind.introspect;
 
+import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.cfg.MapperConfig;
 
 /**
  * Tests Scala-style JVM naming patterns for properties.
@@ -49,13 +51,21 @@
             return null;
         }
 
+        /* Deprecated since 2.9
         @Override
         public boolean hasCreatorAnnotation(Annotated a) {
-            // A placeholder for legitmate creator detection.
+            return (a instanceof AnnotatedConstructor);
+        }
+        */
+
+        @Override
+        public JsonCreator.Mode findCreatorAnnotation(MapperConfig<?> config, Annotated a) {
+            // A placeholder for legitimate creator detection.
             // In Scala, all primary constructors should be creators,
             // but I can't obtain a reference to the AnnotatedClass from the
             // AnnotatedConstructor, so it's simulated here.
-            return (a instanceof AnnotatedConstructor);
+            return (a instanceof AnnotatedConstructor)
+                    ? JsonCreator.Mode.DEFAULT : null;
         }
     }
 
diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/TypeCoercion1592Test.java b/src/test/java/com/fasterxml/jackson/databind/introspect/TypeCoercion1592Test.java
new file mode 100644
index 0000000..c1feb65
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/introspect/TypeCoercion1592Test.java
@@ -0,0 +1,35 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import com.fasterxml.jackson.databind.*;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+// [databind#1592]: allow "coercion" between primitive/wrapper (mostly just ignoring)
+public class TypeCoercion1592Test extends BaseMapTest
+{
+    static class Bean1592
+    {
+        @JsonSerialize(as=Integer.class)
+        public int i;
+
+        @JsonDeserialize(as=Long.class)
+        public long l;
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+
+    public void testTypeCoercion1592() throws Exception
+    {
+        // first, serialize
+        MAPPER.writeValueAsString(new Bean1592());
+        Bean1592 result = MAPPER.readValue("{}", Bean1592.class);
+        assertNotNull(result);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/VisibilityForSerializationTest.java b/src/test/java/com/fasterxml/jackson/databind/introspect/VisibilityForSerializationTest.java
new file mode 100644
index 0000000..949ba6e
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/introspect/VisibilityForSerializationTest.java
@@ -0,0 +1,160 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import java.io.*;
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
+
+/**
+ * Unit tests for checking handling of some of {@link MapperFeature}s
+ * and {@link SerializationFeature}s for serialization.
+ */
+public class VisibilityForSerializationTest
+    extends BaseMapTest
+{
+    /**
+     * Class with one explicitly defined getter, one name-based
+     * auto-detectable getter.
+     */
+    static class GetterClass
+    {
+        @JsonProperty("x") public int getX() { return -2; }
+        public int getY() { return 1; }
+    }
+
+    /**
+     * Another test-class that explicitly disables auto-detection
+     */
+    @JsonAutoDetect(getterVisibility=Visibility.NONE)
+    static class DisabledGetterClass
+    {
+        @JsonProperty("x") public int getX() { return -2; }
+        public int getY() { return 1; }
+    }
+
+    /**
+     * Another test-class that explicitly enables auto-detection
+     */
+    @JsonAutoDetect(isGetterVisibility=Visibility.NONE)
+    static class EnabledGetterClass
+    {
+        @JsonProperty("x") public int getX() { return -2; }
+        public int getY() { return 1; }
+
+        // not auto-detected, since "is getter" auto-detect disabled
+        public boolean isOk() { return true; }
+    }
+
+    /**
+     * One more: only detect "isXxx", not "getXXX"
+     */
+    @JsonAutoDetect(getterVisibility=Visibility.NONE)
+    static class EnabledIsGetterClass
+    {
+        // Won't be auto-detected any more
+        public int getY() { return 1; }
+
+        // but this will be
+        public boolean isOk() { return true; }
+    }
+
+    static class TCls {
+        @JsonProperty("groupname")
+        private String groupname;
+
+        public void setName(String str) {
+            this.groupname = str;
+        }
+        public String getName() {
+            return groupname;
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    public void testGlobalAutoDetection() throws IOException
+    {
+        // First: auto-detection enabled (default):
+        ObjectMapper m = new ObjectMapper();
+        Map<String,Object> result = writeAndMap(m, new GetterClass());
+        assertEquals(2, result.size());
+        assertEquals(Integer.valueOf(-2), result.get("x"));
+        assertEquals(Integer.valueOf(1), result.get("y"));
+
+        // Then auto-detection disabled. But note: we MUST create a new
+        // mapper, since old version of serializer may be cached by now
+        m = new ObjectMapper();
+        m.configure(MapperFeature.AUTO_DETECT_GETTERS, false);
+        result = writeAndMap(m, new GetterClass());
+        assertEquals(1, result.size());
+        assertTrue(result.containsKey("x"));
+    }
+
+    public void testPerClassAutoDetection() throws IOException
+    {
+        // First: class-level auto-detection disabling
+        ObjectMapper m = new ObjectMapper();
+        Map<String,Object> result = writeAndMap(m, new DisabledGetterClass());
+        assertEquals(1, result.size());
+        assertTrue(result.containsKey("x"));
+
+        // And then class-level auto-detection enabling, should override defaults
+        m.configure(MapperFeature.AUTO_DETECT_GETTERS, true);
+        result = writeAndMap(m, new EnabledGetterClass());
+        assertEquals(2, result.size());
+        assertTrue(result.containsKey("x"));
+        assertTrue(result.containsKey("y"));
+    }
+
+    public void testPerClassAutoDetectionForIsGetter() throws IOException
+    {
+        ObjectMapper m = new ObjectMapper();
+        // class level should override
+        m.configure(MapperFeature.AUTO_DETECT_GETTERS, true);
+        m.configure(MapperFeature.AUTO_DETECT_IS_GETTERS, false);
+        Map<String,Object> result = writeAndMap(m, new EnabledIsGetterClass());
+        assertEquals(0, result.size());
+        assertFalse(result.containsKey("ok"));
+    }
+
+    // Simple test verifying that chainable methods work ok...
+    public void testConfigChainability()
+    {
+        ObjectMapper m = new ObjectMapper();
+        assertTrue(m.isEnabled(MapperFeature.AUTO_DETECT_SETTERS));
+        assertTrue(m.isEnabled(MapperFeature.AUTO_DETECT_GETTERS));
+        m.configure(MapperFeature.AUTO_DETECT_SETTERS, false)
+            .configure(MapperFeature.AUTO_DETECT_GETTERS, false);
+        assertFalse(m.isEnabled(MapperFeature.AUTO_DETECT_SETTERS));
+        assertFalse(m.isEnabled(MapperFeature.AUTO_DETECT_GETTERS));
+    }
+
+    public void testVisibilityFeatures() throws Exception
+    {
+        ObjectMapper om = new ObjectMapper();
+        // Only use explicitly specified values to be serialized/deserialized (i.e., JSONProperty).
+        om.configure(MapperFeature.AUTO_DETECT_FIELDS, false);
+        om.configure(MapperFeature.AUTO_DETECT_GETTERS, false);
+        om.configure(MapperFeature.AUTO_DETECT_SETTERS, false);
+        om.configure(MapperFeature.AUTO_DETECT_IS_GETTERS, false);
+        om.configure(MapperFeature.USE_GETTERS_AS_SETTERS, false);
+        om.configure(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS, true);
+        om.configure(MapperFeature.INFER_PROPERTY_MUTATORS, false);
+        om.configure(MapperFeature.USE_ANNOTATIONS, true);
+
+        JavaType javaType = om.getTypeFactory().constructType(TCls.class);        
+        BeanDescription desc = (BeanDescription) om.getSerializationConfig().introspect(javaType);
+        List<BeanPropertyDefinition> props = desc.findProperties();
+        if (props.size() != 1) {
+            fail("Should find 1 property, not "+props.size()+"; properties = "+props);
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsonschema/NewSchemaTest.java b/src/test/java/com/fasterxml/jackson/databind/jsonschema/NewSchemaTest.java
index ea31611..e62a025 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsonschema/NewSchemaTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsonschema/NewSchemaTest.java
@@ -3,13 +3,16 @@
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.util.*;
+import java.util.concurrent.atomic.AtomicReference;
 
+import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonPropertyOrder;
 import com.fasterxml.jackson.annotation.JsonValue;
 import com.fasterxml.jackson.core.JsonParser.NumberType;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.jsonFormatVisitors.*;
 import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
 
 /**
  * Basic tests to exercise low-level support added for JSON Schema module and
@@ -45,12 +48,155 @@
         public EnumMap<TestEnum,Double> weights;
     }
 
+    static class POJOWithScalars {
+        public boolean boo;
+        public byte b;
+        public char c;
+        public short s;
+        public int i;
+        public long l;
+        public float f;
+        public double d;
+
+        public byte[] arrayBoo;
+        public byte[] arrayb;
+        public char[] arrayc;
+        public short[] arrays;
+        public int[] arrayi;
+        public long[] arrayl;
+        public float[] arrayf;
+        public double[] arrayd;
+
+        public Boolean Boo;
+        public Byte B;
+        public Character C;
+        public Short S;
+        public Integer I;
+        public Long L;
+        public Float F;
+        public Double D;
+
+        public TestEnum en;
+        public String str;
+        public String[] strs;
+        public java.util.Date date;
+        public java.util.Calendar calendar;
+    }
+
+    static class POJOWithRefs {
+        public AtomicReference<POJO> maybePOJO;
+
+        public AtomicReference<String> maybeString;
+    }
+
+    // [databind#1793]
+    static class POJOWithJsonValue {
+        private Point[] value;
+
+        @JsonCreator(mode=JsonCreator.Mode.DELEGATING)
+        public POJOWithJsonValue(Point[] v) { value = v; }
+
+        @JsonValue
+        public Point[] serialization() { return value; }
+    }
+
     @JsonPropertyOrder({ "dec", "bigInt" })
     static class Numbers {
         public BigDecimal dec;
         public BigInteger bigInt;
     }
 
+    static class BogusJsonFormatVisitorWrapper
+        extends JsonFormatVisitorWrapper.Base
+    {
+        // Implement handlers just to get more exercise...
+        
+        @Override
+        public JsonObjectFormatVisitor expectObjectFormat(JavaType type) {
+            return new JsonObjectFormatVisitor.Base(getProvider()) {
+                @Override
+                public void property(BeanProperty prop) throws JsonMappingException {
+                    _visit(prop);
+                }
+
+                @Override
+                public void property(String name, JsonFormatVisitable handler,
+                        JavaType propertyTypeHint) { }
+
+                @Override
+                public void optionalProperty(BeanProperty prop) throws JsonMappingException {
+                    _visit(prop);
+                }
+
+                @Override
+                public void optionalProperty(String name, JsonFormatVisitable handler,
+                        JavaType propertyTypeHint) throws JsonMappingException { }
+
+                private void _visit(BeanProperty prop) throws JsonMappingException
+                {
+                    if (!(prop instanceof BeanPropertyWriter)) {
+                        return;
+                    }
+                    BeanPropertyWriter bpw = (BeanPropertyWriter) prop;
+                    JsonSerializer<?> ser = bpw.getSerializer();
+                    final SerializerProvider prov = getProvider();
+                    if (ser == null) {
+                        if (prov == null) {
+                            throw new Error("SerializerProvider missing");
+                        }
+                        ser = prov.findValueSerializer(prop.getType(), prop);
+                    }
+                    // and this just for bit of extra coverage...
+                    if (ser instanceof StdSerializer) {
+                        assertNotNull(((StdSerializer<?>) ser).getSchema(prov, prop.getType()));
+                    }
+                    JsonFormatVisitorWrapper visitor = new JsonFormatVisitorWrapper.Base(getProvider());
+                    ser.acceptJsonFormatVisitor(visitor, prop.getType());
+                }
+            };
+        }
+
+        @Override
+        public JsonArrayFormatVisitor expectArrayFormat(JavaType type) {
+            return new JsonArrayFormatVisitor.Base(getProvider());
+        }
+
+        @Override
+        public JsonStringFormatVisitor expectStringFormat(JavaType type) {
+            return new JsonStringFormatVisitor.Base();
+        }
+
+        @Override
+        public JsonNumberFormatVisitor expectNumberFormat(JavaType type) {
+            return new JsonNumberFormatVisitor.Base();
+        }
+
+        @Override
+        public JsonIntegerFormatVisitor expectIntegerFormat(JavaType type) {
+            return new JsonIntegerFormatVisitor.Base();
+        }
+
+        @Override
+        public JsonBooleanFormatVisitor expectBooleanFormat(JavaType type) {
+            return new JsonBooleanFormatVisitor.Base();
+        }
+
+        @Override
+        public JsonNullFormatVisitor expectNullFormat(JavaType type) {
+            return new JsonNullFormatVisitor.Base();
+        }
+
+        @Override
+        public JsonAnyFormatVisitor expectAnyFormat(JavaType type) {
+            return new JsonAnyFormatVisitor.Base();
+        }
+
+        @Override
+        public JsonMapFormatVisitor expectMapFormat(JavaType type) {
+            return new JsonMapFormatVisitor.Base();
+        }        
+    }
+
     /*
     /**********************************************************
     /* Test methods
@@ -65,9 +211,17 @@
      */
     public void testBasicTraversal() throws Exception
     {
-        MAPPER.acceptJsonFormatVisitor(POJO.class, new JsonFormatVisitorWrapper.Base());
+        MAPPER.acceptJsonFormatVisitor(POJO.class, new BogusJsonFormatVisitorWrapper());
+        MAPPER.acceptJsonFormatVisitor(POJOWithScalars.class, new BogusJsonFormatVisitorWrapper());
+        MAPPER.acceptJsonFormatVisitor(LinkedHashMap.class, new BogusJsonFormatVisitorWrapper());
+        MAPPER.acceptJsonFormatVisitor(ArrayList.class, new BogusJsonFormatVisitorWrapper());
+        MAPPER.acceptJsonFormatVisitor(EnumSet.class, new BogusJsonFormatVisitorWrapper());
+
+        MAPPER.acceptJsonFormatVisitor(POJOWithRefs.class, new BogusJsonFormatVisitorWrapper());
+
+        MAPPER.acceptJsonFormatVisitor(POJOWithJsonValue.class, new BogusJsonFormatVisitorWrapper());
     }
-    
+
     public void testSimpleEnum() throws Exception
     {
         final Set<String> values = new TreeSet<String>();
@@ -125,7 +279,7 @@
         assertEquals(exp, values);
     }
 
-    // [2.7]: Ensure JsonValueFormat serializes/deserializes as expected
+    //  Ensure JsonValueFormat serializes/deserializes as expected
     public void testJsonValueFormatHandling() throws Exception
     {
         // first: serialize using 'toString()', not name
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/AbstracTypeMapping1186Test.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/AbstractTypeMapping1186Test.java
similarity index 88%
rename from src/test/java/com/fasterxml/jackson/databind/jsontype/AbstracTypeMapping1186Test.java
rename to src/test/java/com/fasterxml/jackson/databind/jsontype/AbstractTypeMapping1186Test.java
index 9985dc6..a5a4537 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/AbstracTypeMapping1186Test.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/AbstractTypeMapping1186Test.java
@@ -6,7 +6,7 @@
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.module.SimpleModule;
 
-public class AbstracTypeMapping1186Test extends BaseMapTest
+public class AbstractTypeMapping1186Test extends BaseMapTest
 {
     public interface IContainer<T> {
         @JsonProperty("ts")
@@ -33,7 +33,7 @@
     }
 
     public void testDeserializeMyContainer() throws Exception {
-        Module module = new SimpleModule().addAbstractTypeMapping(IContainer.class, MyContainer.class);
+        SimpleModule module = new SimpleModule().addAbstractTypeMapping(IContainer.class, MyContainer.class);
         final ObjectMapper mapper = new ObjectMapper().registerModule(module);
         String json = "{\"ts\": [ { \"msg\": \"hello\"} ] }";
         final Object o = mapper.readValue(json,
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/DefaultTypingWithPrimitivesTest.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/DefaultTypingWithPrimitivesTest.java
deleted file mode 100644
index 707b33b..0000000
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/DefaultTypingWithPrimitivesTest.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package com.fasterxml.jackson.databind.jsontype;
-
-import com.fasterxml.jackson.annotation.JsonTypeInfo;
-
-import com.fasterxml.jackson.databind.*;
-import com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder;
-
-import java.util.*;
-
-// [databind#1395]: prevent attempts at including type info for primitives
-public class DefaultTypingWithPrimitivesTest extends BaseMapTest
-{
-    static class Data {
-        public long key;
-    }
-
-    public void testDefaultTypingWithLong() throws Exception
-    {
-        Data data = new Data();
-        data.key = 1L;
-        Map<String, Object> mapData = new HashMap<String, Object>();
-        mapData.put("longInMap", 2L);
-        mapData.put("longAsField", data);
-
-        // Configure Jackson to preserve types
-        ObjectMapper mapper = new ObjectMapper();
-        StdTypeResolverBuilder resolver = new StdTypeResolverBuilder();
-        resolver.init(JsonTypeInfo.Id.CLASS, null);
-        resolver.inclusion(JsonTypeInfo.As.PROPERTY);
-        resolver.typeProperty("__t");
-        mapper.setDefaultTyping(resolver);
-        mapper.enable(SerializationFeature.INDENT_OUTPUT);
-
-        // Serialize
-        String json = mapper.writeValueAsString(mapData);
-
-        // Deserialize
-        Map<?,?> result = mapper.readValue(json, Map.class);
-        assertNotNull(result);
-        assertEquals(2, result.size());
-    }
-}
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestSubtypesExistingProperty.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/ExistingPropertyTest.java
similarity index 76%
rename from src/test/java/com/fasterxml/jackson/databind/jsontype/TestSubtypesExistingProperty.java
rename to src/test/java/com/fasterxml/jackson/databind/jsontype/ExistingPropertyTest.java
index 0bb7275..a4a81db 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestSubtypesExistingProperty.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/ExistingPropertyTest.java
@@ -12,18 +12,18 @@
 import com.fasterxml.jackson.databind.BaseMapTest;
 import com.fasterxml.jackson.databind.ObjectMapper;
 
-public class TestSubtypesExistingProperty extends BaseMapTest {
-
+public class ExistingPropertyTest extends BaseMapTest
+{
     /**
      * Polymorphic base class - existing property as simple property on subclasses
      */
-	@JsonTypeInfo(use = Id.NAME, include = As.EXISTING_PROPERTY, property = "type",
-	        visible=true)
-	@JsonSubTypes({
-		@Type(value = Apple.class, name = "apple") ,
-		@Type(value = Orange.class, name = "orange") 
-		})
-	static abstract class Fruit {
+    @JsonTypeInfo(use = Id.NAME, include = As.EXISTING_PROPERTY, property = "type",
+            visible=true)
+    @JsonSubTypes({
+        @Type(value = Apple.class, name = "apple") ,
+        @Type(value = Orange.class, name = "orange") 
+    })
+    static abstract class Fruit {
         public String name;
         protected Fruit(String n)  { name = n; }
     }
@@ -34,20 +34,20 @@
         public int seedCount;
         public String type;
 
-        private Apple() { super(null);; }
+        private Apple() { super(null); }
         public Apple(String name, int b) {
             super(name);
             seedCount = b;
             type = "apple";
         }
     }
-    
+
     @JsonTypeName("orange")
     static class Orange extends Fruit
     {
         public String color;
         public String type;
-        
+
         private Orange() { super(null); }
         public Orange(String name, String c) {
             super(name);
@@ -61,7 +61,7 @@
         public FruitWrapper() {}
         public FruitWrapper(Fruit f) { fruit = f; }
     }
-    
+
     /**
      * Polymorphic base class - existing property forced by abstract method
      */
@@ -94,7 +94,7 @@
         	return "doggie";
         }        
     }
-    
+
     @JsonTypeName("kitty")
     static class Cat extends Animal
     {
@@ -118,7 +118,6 @@
         public AnimalWrapper(Animal a) { animal = a; }
     }
 
-
     /**
      * Polymorphic base class - existing property NOT forced by abstract method on base class
      */
@@ -147,7 +146,7 @@
         	return "accord";
         }        
     }
-    
+
     @JsonTypeName("camry")
     static class Camry extends Car
     {
@@ -169,8 +168,26 @@
         public CarWrapper() {}
         public CarWrapper(Car c) { car = c; }
     }
-    
-    private final ObjectMapper MAPPER = new ObjectMapper();
+
+    // for [databind#1635]
+
+    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
+            include = JsonTypeInfo.As.EXISTING_PROPERTY,
+            // IMPORTANT! Must be defined as `visible`
+            visible=true,
+            property = "type",
+            defaultImpl=Bean1635Default.class)
+    @JsonSubTypes({ @JsonSubTypes.Type(Bean1635A.class) })
+    static class Bean1635 {
+        public ABC type;
+    }
+
+    @JsonTypeName("A")
+    static class Bean1635A extends Bean1635 {
+        public int value;
+    }
+
+    static class Bean1635Default extends Bean1635 { }
 
     /*
     /**********************************************************
@@ -178,39 +195,41 @@
     /**********************************************************
      */
 
-	private static final Orange mandarin = new Orange("Mandarin Orange", "orange");
-	private static final String mandarinJson = "{\"name\":\"Mandarin Orange\",\"color\":\"orange\",\"type\":\"orange\"}";	
-	private static final Apple pinguo = new Apple("Apple-A-Day", 16);
-	private static final String pinguoJson = "{\"name\":\"Apple-A-Day\",\"seedCount\":16,\"type\":\"apple\"}";
-	private static final FruitWrapper pinguoWrapper = new FruitWrapper(pinguo);
-	private static final String pinguoWrapperJson = "{\"fruit\":" + pinguoJson + "}";
-	private static final List<Fruit> fruitList = Arrays.asList(pinguo, mandarin);
-	private static final String fruitListJson = "[" + pinguoJson + "," + mandarinJson + "]";
+    private static final Orange mandarin = new Orange("Mandarin Orange", "orange");
+    private static final String mandarinJson = "{\"name\":\"Mandarin Orange\",\"color\":\"orange\",\"type\":\"orange\"}";	
+    private static final Apple pinguo = new Apple("Apple-A-Day", 16);    
+    private static final String pinguoJson = "{\"name\":\"Apple-A-Day\",\"seedCount\":16,\"type\":\"apple\"}";
+    private static final FruitWrapper pinguoWrapper = new FruitWrapper(pinguo);
+    private static final String pinguoWrapperJson = "{\"fruit\":" + pinguoJson + "}";
+    private static final List<Fruit> fruitList = Arrays.asList(pinguo, mandarin);
+    private static final String fruitListJson = "[" + pinguoJson + "," + mandarinJson + "]";
 
-	private static final Cat beelzebub = new Cat("Beelzebub", "tabby");
-	private static final String beelzebubJson = "{\"name\":\"Beelzebub\",\"furColor\":\"tabby\",\"type\":\"kitty\"}";	
-	private static final Dog rover = new Dog("Rover", 42);
-	private static final String roverJson = "{\"name\":\"Rover\",\"boneCount\":42,\"type\":\"doggie\"}";
-	private static final AnimalWrapper beelzebubWrapper = new AnimalWrapper(beelzebub);
-	private static final String beelzebubWrapperJson = "{\"animal\":" + beelzebubJson + "}";
-	private static final List<Animal> animalList = Arrays.asList(beelzebub, rover);
-	private static final String animalListJson = "[" + beelzebubJson + "," + roverJson + "]";
+    private static final Cat beelzebub = new Cat("Beelzebub", "tabby");
+    private static final String beelzebubJson = "{\"name\":\"Beelzebub\",\"furColor\":\"tabby\",\"type\":\"kitty\"}";	
+    private static final Dog rover = new Dog("Rover", 42);
+    private static final String roverJson = "{\"name\":\"Rover\",\"boneCount\":42,\"type\":\"doggie\"}";
+    private static final AnimalWrapper beelzebubWrapper = new AnimalWrapper(beelzebub);
+    private static final String beelzebubWrapperJson = "{\"animal\":" + beelzebubJson + "}";
+    private static final List<Animal> animalList = Arrays.asList(beelzebub, rover);
+    private static final String animalListJson = "[" + beelzebubJson + "," + roverJson + "]";
 
-	private static final Camry camry = new Camry("Sweet Ride", "candy-apple-red");
-	private static final String camryJson = "{\"name\":\"Sweet Ride\",\"exteriorColor\":\"candy-apple-red\",\"type\":\"camry\"}";	
-	private static final Accord accord = new Accord("Road Rage", 6);
-	private static final String accordJson = "{\"name\":\"Road Rage\",\"speakerCount\":6,\"type\":\"accord\"}";
-	private static final CarWrapper camryWrapper = new CarWrapper(camry);
-	private static final String camryWrapperJson = "{\"car\":" + camryJson + "}";
-	private static final List<Car> carList = Arrays.asList(camry, accord);
-	private static final String carListJson = "[" + camryJson + "," + accordJson + "]";
+    private static final Camry camry = new Camry("Sweet Ride", "candy-apple-red");
+    private static final String camryJson = "{\"name\":\"Sweet Ride\",\"exteriorColor\":\"candy-apple-red\",\"type\":\"camry\"}";	
+    private static final Accord accord = new Accord("Road Rage", 6);
+    private static final String accordJson = "{\"name\":\"Road Rage\",\"speakerCount\":6,\"type\":\"accord\"}";
+    private static final CarWrapper camryWrapper = new CarWrapper(camry);
+    private static final String camryWrapperJson = "{\"car\":" + camryJson + "}";
+    private static final List<Car> carList = Arrays.asList(camry, accord);
+    private static final String carListJson = "[" + camryJson + "," + accordJson + "]";
 
     /*
     /**********************************************************
-    /* Unit tests
+    /* Test methods
     /**********************************************************
      */
 
+    private final ObjectMapper MAPPER = new ObjectMapper();
+
     /**
      * Fruits - serialization tests for simple property on sub-classes
      */
@@ -354,10 +373,10 @@
         assertEquals(accord.name, result.get("name"));
         assertEquals(accord.speakerCount, result.get("speakerCount"));
         assertEquals(accord.getType(), result.get("type"));
-        
+
         String camrySerialized = MAPPER.writeValueAsString(camry);
         assertEquals(camrySerialized, camryJson);
-        
+
         String accordSerialized = MAPPER.writeValueAsString(accord);
         assertEquals(accordSerialized, accordJson);
         
@@ -400,4 +419,25 @@
         assertTrue(result instanceof Accord);
         assertSame(result.getClass(), Accord.class);
     }
-}    
+
+    // for [databind#1635]: simple usage
+    public void testExistingEnumTypeId() throws Exception
+    {
+        Bean1635 result = MAPPER.readValue(aposToQuotes("{'value':3, 'type':'A'}"),
+                Bean1635.class);
+        assertEquals(Bean1635A.class, result.getClass());
+        Bean1635A bean = (Bean1635A) result;
+        assertEquals(3, bean.value);
+        assertEquals(ABC.A, bean.type);
+    }
+
+    // for [databind#1635]: verify that `defaultImpl` does not block assignment of
+    // type id
+    public void testExistingEnumTypeIdViaDefault() throws Exception
+    {
+        Bean1635 result = MAPPER.readValue(aposToQuotes("{'type':'C'}"),
+                Bean1635.class);
+        assertEquals(Bean1635Default.class, result.getClass());
+        assertEquals(ABC.C, result.type);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/GenericTypeId1735Test.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/GenericTypeId1735Test.java
index 673d4c8..1a87f8f 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/GenericTypeId1735Test.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/GenericTypeId1735Test.java
@@ -3,6 +3,7 @@
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
 
 import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.exc.InvalidTypeIdException;
 
 // for [databind#1735]:
 public class GenericTypeId1735Test extends BaseMapTest
@@ -44,8 +45,9 @@
 "{'w':{'type':'"+NEF_CLASS+"'}}"),
                     Wrapper1735.class);
             fail("Should not pass");
-        } catch (JsonMappingException e) {
-            verifyException(e, "not subtype of");
+        } catch (InvalidTypeIdException e) {
+            verifyException(e, "could not resolve type id");
+            verifyException(e, "not a subtype");
         }
     }
 
@@ -57,8 +59,9 @@
 "{'w':{'type':'java.util.HashMap<java.lang.String,java.lang.String>'}}"),
                     Wrapper1735.class);
             fail("Should not pass");
-        } catch (JsonMappingException e) {
-            verifyException(e, "not subtype of");
+        } catch (InvalidTypeIdException e) {
+            verifyException(e, "could not resolve type id");
+            verifyException(e, "not a subtype");
         }
     }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestNoTypeInfo.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/NoTypeInfoTest.java
similarity index 87%
rename from src/test/java/com/fasterxml/jackson/databind/jsontype/TestNoTypeInfo.java
rename to src/test/java/com/fasterxml/jackson/databind/jsontype/NoTypeInfoTest.java
index 3678179..65054d5 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestNoTypeInfo.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/NoTypeInfoTest.java
@@ -5,7 +5,7 @@
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
 
-public class TestNoTypeInfo extends BaseMapTest
+public class NoTypeInfoTest extends BaseMapTest
 {
     @JsonTypeInfo(use=JsonTypeInfo.Id.NONE)
     @JsonDeserialize(as=NoType.class)
@@ -15,17 +15,16 @@
     final static class NoType implements NoTypeInterface {
         public int a = 3;
     }
-    
+
     /*
     /**********************************************************
-    /* Unit tests
+    /* Test methods
     /**********************************************************
      */
 
-    // for [JACKSON-746]
     public void testWithIdNone() throws Exception
     {
-        final ObjectMapper mapper = new ObjectMapper();
+        final ObjectMapper mapper = newObjectMapper();
         mapper.enableDefaultTyping();
         // serialize without type info
         String json = mapper.writeValueAsString(new NoType());
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/SubTypeResolution1964Test.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/SubTypeResolution1964Test.java
new file mode 100644
index 0000000..616e316
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/SubTypeResolution1964Test.java
@@ -0,0 +1,106 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Test for [databind#1964], wherein slightly incompatible type hierarchy,
+ * where `Map` key is downcast from `String` to `Object` (via use of "raw"
+ * types to force compiler to ignore incompatibility) causes exception
+ * during serialization. Although ideally code would not force round peg
+ * through square hole, it makes sense to add specific exception to allow
+ * such downcast just for Map key types (for now at least).
+ */
+@SuppressWarnings("serial")
+public class SubTypeResolution1964Test extends BaseMapTest
+{
+    // [databind#1964]
+    static class AccessModel {
+        private Map<String, Collection<String>> repositoryPrivileges;
+
+        public AccessModel() {
+            repositoryPrivileges = new HashMap<>();
+        }
+
+        // 19-Apr-2018, tatu; this would prevent issues
+//        @JsonSerialize(typing = JsonSerialize.Typing.STATIC)
+        public Map<String, Collection<String>> getRepositoryPrivileges() {
+            return repositoryPrivileges;
+        }
+
+        public void setRepositoryPrivileges(Map<String, Collection<String>> repositoryPrivileges) {
+            this.repositoryPrivileges = repositoryPrivileges;
+        }
+    }
+    static class CustomMap<T> extends LinkedHashMap<Object, T> { }
+
+    // [databind#2034]: specialization from `Object` to other types probably should
+    // just be allowed (at least in serialization case)
+    interface Dummy {
+      List<String> getStrings();
+    }
+
+    static class MetaModel<M, B> extends AbstractMetaValue<M, M, B> {
+
+        @JsonProperty
+        protected final Map<String, AbstractMetaValue<M, ?, B>> attributes = new HashMap<>();
+
+        public <V> ListMetaAttribute<M, V, B> describeList(final String attributeName) {
+          final ListMetaAttribute<M, V, B> metaAttribute = new ListMetaAttribute<>();
+          attributes.put(attributeName, metaAttribute);
+          return metaAttribute;
+        }
+      }
+
+    static abstract class AbstractMetaValue<M, V, B> {
+        public int getBogus() { return 3; }
+    }
+
+    static class ListMetaAttribute<M, V, B> extends MetaAttribute<M, List<V>, B> {
+        public ListMetaAttribute() { }
+    }
+
+    static class MetaAttribute<M, V, B> extends AbstractMetaValue<M, V, B> {
+        public MetaAttribute() { }
+      }
+    
+    /*
+    /**********************************************************************
+    /* Unit tests
+    /**********************************************************************
+     */
+
+    final ObjectMapper MAPPER = newObjectMapper();
+
+    // [databind#1964]
+    public void testTypeCompatibility1964() throws Exception
+    {
+        // Important! Must use raw type since assignment requires effectively
+        // casting due incompatible type parameters.
+        @SuppressWarnings({ "unchecked", "rawtypes" })
+        Map<String, Collection<String>> repoPrivilegesMap = new CustomMap();
+        String key = "/storages/storage0/releases";
+        Collection<String> values = new HashSet<>();
+        values.add("ARTIFACTS_RESOLVE");
+        repoPrivilegesMap.put(key, values);
+        
+        AccessModel accessModel = new AccessModel();
+        accessModel.setRepositoryPrivileges(repoPrivilegesMap);
+
+        String jsonStr = MAPPER.writeValueAsString(accessModel);
+        // ... could/should verify more, perhaps, but for now let it be.
+        assertNotNull(jsonStr);
+    }
+
+    // [databind#2034]
+    public void testTypeSpecialization2034() throws Exception
+    {
+        MetaModel<Dummy, Dummy> metaModel = new MetaModel<>();
+        metaModel.describeList("a1");
+        String jsonStr = MAPPER.writeValueAsString(metaModel);
+        // ... could/should verify more, perhaps, but for now let it be.
+        assertNotNull(jsonStr);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestAbstractTypeNames.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestAbstractTypeNames.java
index 2635b12..b98ce41 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestAbstractTypeNames.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestAbstractTypeNames.java
@@ -88,14 +88,13 @@
             public String toString() { return "sub!"; }
         };
     }
-    
+
     /*
     /**********************************************************
-    /* Unit tests
+    /* Test methods
     /**********************************************************
      */
 
-    // Testing [JACKSON-498], partial fix
     public void testEmptyCollection() throws Exception
     {
         ObjectMapper mapper = new ObjectMapper();
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestBaseTypeAsDefault.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestBaseTypeAsDefault.java
new file mode 100644
index 0000000..407d1e4
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestBaseTypeAsDefault.java
@@ -0,0 +1,91 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.MapperFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import java.io.IOException;
+
+public class TestBaseTypeAsDefault extends BaseMapTest
+{
+    @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "@class")
+    static class Parent {
+    }
+
+
+    static class Child extends Parent {
+    }
+
+
+    @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "@class", defaultImpl = ChildOfChild.class)
+    static abstract class AbstractParentWithDefault {
+    }
+
+
+    static class ChildOfAbstract extends AbstractParentWithDefault {
+    }
+
+    static class ChildOfChild extends ChildOfAbstract {
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    protected ObjectMapper MAPPER_WITH_BASE = new ObjectMapper()
+                .enable(MapperFeature.USE_BASE_TYPE_AS_DEFAULT_IMPL);
+
+    protected ObjectMapper MAPPER_WITHOUT_BASE = new ObjectMapper()
+            .disable(MapperFeature.USE_BASE_TYPE_AS_DEFAULT_IMPL);
+    
+    public void testPositiveForParent() throws IOException {
+        Object o = MAPPER_WITH_BASE.readerFor(Parent.class).readValue("{}");
+        assertEquals(o.getClass(), Parent.class);
+    }
+
+    public void testPositiveForChild() throws IOException {
+        Object o = MAPPER_WITH_BASE.readerFor(Child.class).readValue("{}");
+        assertEquals(o.getClass(), Child.class);
+    }
+
+    public void testNegativeForParent() throws IOException {
+        try {
+            /*Object o =*/ MAPPER_WITHOUT_BASE.readerFor(Parent.class).readValue("{}");
+            fail("Should not pass");
+        } catch (JsonMappingException ex) {
+            assertTrue(ex.getMessage().contains("missing type id property '@class'"));
+        }
+    }
+
+    public void testNegativeForChild() throws IOException {
+        try {
+            /*Object o =*/ MAPPER_WITHOUT_BASE.readerFor(Child.class).readValue("{}");
+            fail("Should not pass");
+        } catch (JsonMappingException ex) {
+            assertTrue(ex.getMessage().contains("missing type id property '@class'"));
+        }
+    }
+
+    public void testConversionForAbstractWithDefault() throws IOException {
+        // should pass shouldn't it?
+        Object o = MAPPER_WITH_BASE.readerFor(AbstractParentWithDefault.class).readValue("{}");
+        assertEquals(o.getClass(), ChildOfChild.class);
+    }
+
+    public void testPositiveWithTypeSpecification() throws IOException {
+        Object o = MAPPER_WITH_BASE.readerFor(Parent.class)
+                .readValue("{\"@class\":\""+Child.class.getName()+"\"}");
+        assertEquals(o.getClass(), Child.class);
+    }
+
+    public void testPositiveWithManualDefault() throws IOException {
+        Object o = MAPPER_WITH_BASE.readerFor(ChildOfAbstract.class).readValue("{}");
+
+        assertEquals(o.getClass(), ChildOfChild.class);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestPolymorphicDeserialization676.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPolymorphicDeserialization676.java
similarity index 98%
rename from src/test/java/com/fasterxml/jackson/databind/deser/TestPolymorphicDeserialization676.java
rename to src/test/java/com/fasterxml/jackson/databind/jsontype/TestPolymorphicDeserialization676.java
index 945a5e7..d6b1bcc 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestPolymorphicDeserialization676.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPolymorphicDeserialization676.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.deser;
+package com.fasterxml.jackson.databind.jsontype;
 
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPolymorphicWithDefaultImpl.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPolymorphicWithDefaultImpl.java
index 439b7d6..7d260c7 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPolymorphicWithDefaultImpl.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPolymorphicWithDefaultImpl.java
@@ -6,6 +6,7 @@
 
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.annotation.NoClass;
+import com.fasterxml.jackson.databind.exc.InvalidTypeIdException;
 
 /**
  * Unit tests related to specialized handling of "default implementation"
@@ -264,9 +265,8 @@
         try {
             r.readValue("{ \"value\": \"\" }");
             fail("Expected " + JsonMappingException.class);
-        } catch (JsonMappingException e) {
-            verifyException(e, "missing property 'type'");
-            verifyException(e, "contain type id");
+        } catch (InvalidTypeIdException e) {
+            verifyException(e, "missing type id property 'type'");
         }
     }
 
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPolymorphicWithDefaultImpl1565.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPolymorphicWithDefaultImpl1565.java
new file mode 100644
index 0000000..26e7286
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPolymorphicWithDefaultImpl1565.java
@@ -0,0 +1,94 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+
+public class TestPolymorphicWithDefaultImpl1565 extends BaseMapTest
+{
+    // [databind#1565]
+    @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY,
+        property="typeInfo",  defaultImpl = CBaseClass1565.class)
+    @JsonSubTypes({
+        @JsonSubTypes.Type(CDerived1565.class)
+    })
+    public static interface CTestInterface1565
+    {
+         public String getName();
+         public void setName(String name);
+         public String getTypeInfo();
+    }
+
+    static class CBaseClass1565 implements CTestInterface1565
+    {
+         private String mName;
+         
+         @Override
+         public String getName() {
+              return(mName);
+         }
+
+         @Override
+         public void setName(String name) {
+              mName = name;
+         }
+
+         @Override
+         public String getTypeInfo() {
+              return "base";
+         }
+    }
+
+    @JsonTypeName("derived")
+    static class CDerived1565 extends CBaseClass1565
+    {
+         public String description;
+
+         @Override
+         public String getTypeInfo() {
+              return "derived";
+         }
+    }
+
+    // [databind#1861]
+    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = DefaultImpl1861.class)
+    @JsonSubTypes({
+            @JsonSubTypes.Type(name = "a", value = Impl1861A.class)
+    })
+    static abstract class Bean1861 {
+        public String base;
+    }
+
+    static class DefaultImpl1861 extends Bean1861 {
+        public int id;
+    }
+
+    static class Impl1861A extends Bean1861 {
+        public int valueA;
+    }
+
+    /*
+    /**********************************************************************
+    /* Test methods
+    /**********************************************************************
+     */
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+
+    // [databind#1565]
+    public void testIncompatibleDefaultImpl1565() throws Exception
+    {
+        String value = "{\"typeInfo\": \"derived\", \"name\": \"John\", \"description\": \"Owner\"}";
+        CDerived1565 result = MAPPER.readValue(value, CDerived1565.class);
+        assertNotNull(result);
+    }
+
+    // [databind#1861]
+    public void testWithIncompatibleTargetType1861() throws Exception
+    {
+        // Should allow deserialization even if `defaultImpl` incompatible
+        Impl1861A result = MAPPER.readValue(aposToQuotes("{'type':'a','base':'foo','valueA':3}"),
+                Impl1861A.class);
+        assertNotNull(result);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPropertyTypeInfo.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPropertyTypeInfo.java
index 484f6cc..5d68ce8 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPropertyTypeInfo.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPropertyTypeInfo.java
@@ -9,16 +9,19 @@
 
 /**
  * Testing to verify that {@link JsonTypeInfo} works
- * for properties as well as types (see [JACKSON-280] for details)
+ * for properties as well as types.
  */
 @SuppressWarnings("serial")
 public class TestPropertyTypeInfo extends BaseMapTest
 {
-    /*
-    /**********************************************************
-    /* Helper types
-    /**********************************************************
-     */
+    protected static class BooleanValue {
+        public Boolean b;
+
+        @JsonCreator
+        public BooleanValue(Boolean value) { b = value; }
+
+        @JsonValue public Boolean value() { return b; }
+    }
 
     static class FieldWrapperBean
     {
@@ -120,7 +123,7 @@
     {
         ObjectMapper mapper = new ObjectMapper();
         MethodWrapperBeanList list = new MethodWrapperBeanList();
-        list.add(new MethodWrapperBean(new BooleanWrapper(true)));
+        list.add(new MethodWrapperBean(new BooleanValue(true)));
         list.add(new MethodWrapperBean(new StringWrapper("x")));
         list.add(new MethodWrapperBean(new OtherBean()));
         String json = mapper.writeValueAsString(list);
@@ -128,8 +131,8 @@
         assertNotNull(result);
         assertEquals(3, result.size());
         MethodWrapperBean bean = result.get(0);
-        assertEquals(BooleanWrapper.class, bean.value.getClass());
-        assertEquals(((BooleanWrapper) bean.value).b, Boolean.TRUE);
+        assertEquals(BooleanValue.class, bean.value.getClass());
+        assertEquals(((BooleanValue) bean.value).b, Boolean.TRUE);
         bean = result.get(1);
         assertEquals(StringWrapper.class, bean.value.getClass());
         assertEquals(((StringWrapper) bean.value).str, "x");
@@ -141,15 +144,15 @@
     {
         ObjectMapper mapper = new ObjectMapper();
         FieldWrapperBeanArray array = new FieldWrapperBeanArray(new
-                FieldWrapperBean[] { new FieldWrapperBean(new BooleanWrapper(true)) });
+                FieldWrapperBean[] { new FieldWrapperBean(new BooleanValue(true)) });
         String json = mapper.writeValueAsString(array);
         FieldWrapperBeanArray result = mapper.readValue(json, FieldWrapperBeanArray.class);
         assertNotNull(result);
         FieldWrapperBean[] beans = result.beans;
         assertEquals(1, beans.length);
         FieldWrapperBean bean = beans[0];
-        assertEquals(BooleanWrapper.class, bean.value.getClass());
-        assertEquals(((BooleanWrapper) bean.value).b, Boolean.TRUE);
+        assertEquals(BooleanValue.class, bean.value.getClass());
+        assertEquals(((BooleanValue) bean.value).b, Boolean.TRUE);
     }
 
     public void testSimpleArrayMethod() throws Exception
@@ -187,7 +190,7 @@
     {
         ObjectMapper mapper = new ObjectMapper();
         MethodWrapperBeanMap map = new MethodWrapperBeanMap();
-        map.put("xyz", new MethodWrapperBean(new BooleanWrapper(true)));
+        map.put("xyz", new MethodWrapperBean(new BooleanValue(true)));
         String json = mapper.writeValueAsString(map);
         MethodWrapperBeanMap result = mapper.readValue(json, MethodWrapperBeanMap.class);
         assertNotNull(result);
@@ -195,7 +198,7 @@
         MethodWrapperBean bean = result.get("xyz");
         assertNotNull(bean);
         Object ob = bean.value;
-        assertEquals(BooleanWrapper.class, ob.getClass());
-        assertEquals(((BooleanWrapper) ob).b, Boolean.TRUE);
+        assertEquals(BooleanValue.class, ob.getClass());
+        assertEquals(((BooleanValue) ob).b, Boolean.TRUE);
     }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestScalars.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestScalars.java
index c08bf7e..89481bf 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestScalars.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestScalars.java
@@ -42,14 +42,14 @@
             return this;
         }
     }
-    
+
     /*
     /**********************************************************
     /* Unit tests
     /**********************************************************
      */
 
-    final ObjectMapper MAPPER = new ObjectMapper();
+    final ObjectMapper MAPPER = newObjectMapper();
     
     /**
      * Ensure that per-property dynamic types work, both for "native" types
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestSubtypes.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestSubtypes.java
index 4450c04..5517a2c 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestSubtypes.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestSubtypes.java
@@ -2,6 +2,9 @@
 
 
 import com.fasterxml.jackson.core.Version;
+
+import java.util.*;
+
 import com.fasterxml.jackson.annotation.JsonSubTypes;
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
 import com.fasterxml.jackson.annotation.JsonTypeName;
@@ -9,6 +12,7 @@
 import com.fasterxml.jackson.databind.JsonMappingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.databind.exc.InvalidTypeIdException;
 import com.fasterxml.jackson.databind.jsontype.NamedType;
 import com.fasterxml.jackson.databind.module.SimpleModule;
 
@@ -176,7 +180,7 @@
         assertSame(SubC.class, result.value.getClass());
     }
 
-    // [JACKSON-748]: also works via modules
+    // also works via modules
     public void testSubtypesViaModule() throws Exception
     {
         ObjectMapper mapper = new ObjectMapper();
@@ -186,6 +190,19 @@
         String json = mapper.writeValueAsString(new PropertyBean(new SubC()));
         PropertyBean result = mapper.readValue(json, PropertyBean.class);
         assertSame(SubC.class, result.value.getClass());
+
+        // and as per [databind#1653]:
+        mapper = new ObjectMapper();
+        module = new SimpleModule();
+        List<Class<?>> l = new ArrayList<>();
+        l.add(SubB.class);
+        l.add(SubC.class);
+        l.add(SubD.class);
+        module.registerSubtypes(l);
+        mapper.registerModule(module);
+        json = mapper.writeValueAsString(new PropertyBean(new SubC()));
+        result = mapper.readValue(json, PropertyBean.class);
+        assertSame(SubC.class, result.value.getClass());
     }
     
     public void testSerialization() throws Exception
@@ -282,8 +299,8 @@
         try {
             MAPPER.readValue(JSON, SuperTypeWithoutDefault.class);
             fail("Expected an exception");
-        } catch (JsonMappingException e) {
-            verifyException(e, "missing property");
+        } catch (InvalidTypeIdException e) {
+            verifyException(e, "missing type id property '#type'");
         }
 
         // but then succeed when we register default impl
@@ -330,9 +347,11 @@
             MAPPER.readValue(aposToQuotes("{'value':['"
                     +TheBomb.class.getName()+"',{'a':13}] }"), DateWrapper.class);
             fail("Should not pass");
-        } catch (JsonMappingException e) {
-            verifyException(e, "not subtype of");
+        } catch (InvalidTypeIdException e) {
+            verifyException(e, "not a subtype");
             verifyException(e, TheBomb.class.getName());
+        } catch (Exception e) {
+            fail("Should have hit `InvalidTypeIdException`, not `"+e.getClass().getName()+"`: "+e);
         }
     }
 
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypeNames.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypeNames.java
index cfe6a6b..735b22d 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypeNames.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypeNames.java
@@ -7,19 +7,31 @@
 import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
 import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
 import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
+
 import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.jsontype.impl.StdSubtypeResolver;
 import com.fasterxml.jackson.databind.type.TypeFactory;
 
 /**
  * Separate tests for verifying that "type name" type id mechanism
  * works.
- * 
- * @author tatu
  */
 public class TestTypeNames extends BaseMapTest
 {
     @SuppressWarnings("serial")
     static class AnimalMap extends LinkedHashMap<String,Animal> { }
+
+    @JsonTypeInfo(property = "type", include = JsonTypeInfo.As.PROPERTY, use = JsonTypeInfo.Id.NAME)
+    @JsonSubTypes({
+              @JsonSubTypes.Type(value = A1616.class,name = "A"),
+              @JsonSubTypes.Type(value = B1616.class)
+    })
+    static abstract class Base1616 { }
+
+    static class A1616 extends Base1616 { }
+
+    @JsonTypeName("B")
+    static class B1616 extends Base1616 { }
     
     /*
     /**********************************************************
@@ -27,31 +39,48 @@
     /**********************************************************
      */
 
+    private final ObjectMapper MAPPER = objectMapper();
+
+    public void testBaseTypeId1616() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        Collection<NamedType> subtypes = new StdSubtypeResolver().collectAndResolveSubtypesByTypeId(
+                mapper.getDeserializationConfig(),
+                // note: `null` is fine here as `AnnotatedMember`:
+                null,
+                mapper.constructType(Base1616.class));
+        assertEquals(2, subtypes.size());
+        Set<String> ok = new HashSet<>(Arrays.asList("A", "B"));
+        for (NamedType type : subtypes) {
+            String id = type.getName();
+            if (!ok.contains(id)) {
+                fail("Unexpected id '"+id+"' (mapping to: "+type.getType()+"), should be one of: "+ok);
+            }
+        }
+    }
+    
     public void testSerialization() throws Exception
     {
-        ObjectMapper m = new ObjectMapper();
-
         // Note: need to use wrapper array just so that we can define
         // static type on serialization. If we had root static types,
         // could use those; but at the moment root type is dynamic
         
         assertEquals("[{\"doggy\":{\"name\":\"Spot\",\"ageInYears\":3}}]",
-                m.writeValueAsString(new Animal[] { new Dog("Spot", 3) }));
+                MAPPER.writeValueAsString(new Animal[] { new Dog("Spot", 3) }));
         assertEquals("[{\"MaineCoon\":{\"name\":\"Belzebub\",\"purrs\":true}}]",
-                m.writeValueAsString(new Animal[] { new MaineCoon("Belzebub", true)}));
+                MAPPER.writeValueAsString(new Animal[] { new MaineCoon("Belzebub", true)}));
     }
 
     public void testRoundTrip() throws Exception
     {
-        ObjectMapper m = new ObjectMapper();
         Animal[] input = new Animal[] {
                 new Dog("Odie", 7),
                 null,
                 new MaineCoon("Piru", false),
                 new Persian("Khomeini", true)
         };
-        String json = m.writeValueAsString(input);
-        List<Animal> output = m.readValue(json,
+        String json = MAPPER.writeValueAsString(input);
+        List<Animal> output = MAPPER.readValue(json,
                 TypeFactory.defaultInstance().constructCollectionType(ArrayList.class, Animal.class));
         assertEquals(input.length, output.size());
         for (int i = 0, len = input.length; i < len; ++i) {
@@ -62,12 +91,11 @@
 
     public void testRoundTripMap() throws Exception
     {
-        ObjectMapper m = new ObjectMapper();
         AnimalMap input = new AnimalMap();
         input.put("venla", new MaineCoon("Venla", true));
         input.put("ama", new Dog("Amadeus", 13));
-        String json = m.writeValueAsString(input);
-        AnimalMap output = m.readValue(json, AnimalMap.class);
+        String json = MAPPER.writeValueAsString(input);
+        AnimalMap output = MAPPER.readValue(json, AnimalMap.class);
         assertNotNull(output);
         assertEquals(AnimalMap.class, output.getClass());
         assertEquals(input.size(), output.size());
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedArraySerialization.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedArraySerialization.java
index a7ddf02..3b416bd 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedArraySerialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedArraySerialization.java
@@ -70,13 +70,14 @@
     /**********************************************************
      */
 
+    private final ObjectMapper MAPPER = new ObjectMapper();
+
     public void testListWithPolymorphic() throws Exception
     {
-        ObjectMapper mapper = new ObjectMapper();
         BeanListWrapper beans = new BeanListWrapper();
-        assertEquals("{\"beans\":[{\"@type\":\"bean\",\"x\":0}]}", mapper.writeValueAsString(beans));
+        assertEquals("{\"beans\":[{\"@type\":\"bean\",\"x\":0}]}", MAPPER.writeValueAsString(beans));
         // Related to [JACKSON-364]
-        ObjectWriter w = mapper.writerWithView(Object.class);
+        ObjectWriter w = MAPPER.writerWithView(Object.class);
         assertEquals("{\"beans\":[{\"@type\":\"bean\",\"x\":0}]}", w.writeValueAsString(beans));
     }
     
@@ -86,7 +87,8 @@
         input.add(5);
         input.add(13);
         // uses WRAPPER_ARRAY inclusion:
-        assertEquals("[\""+TypedList.class.getName()+"\",[5,13]]", serializeAsString(input));
+        assertEquals("[\""+TypedList.class.getName()+"\",[5,13]]",
+                MAPPER.writeValueAsString(input));
     }
     
     // Similar to above, but this time let's request adding type info
@@ -99,7 +101,7 @@
         input.add("a");
         input.add("b");
         assertEquals("[\""+TypedListAsProp.class.getName()+"\",[\"a\",\"b\"]]",
-                serializeAsString(input));
+                MAPPER.writeValueAsString(input));
     }
 
     public void testStringListAsObjectWrapper() throws Exception
@@ -113,7 +115,7 @@
         // annotations
         String expName = "TestTypedArraySerialization$TypedListAsWrapper";
         assertEquals("{\""+expName+"\":[true,null,false]}",
-                serializeAsString(input));
+                MAPPER.writeValueAsString(input));
     }
 
     /*
@@ -128,7 +130,7 @@
         m.addMixIn(int[].class, WrapperMixIn.class);
         int[] input = new int[] { 1, 2, 3 };
         String clsName = int[].class.getName();
-        assertEquals("{\""+clsName+"\":[1,2,3]}", serializeAsString(m, input));
+        assertEquals("{\""+clsName+"\":[1,2,3]}", m.writeValueAsString(input));
     }
 
     /*
@@ -139,16 +141,14 @@
 
     public void testGenericArray() throws Exception
     {
-        ObjectMapper m;
         final A[] input = new A[] { new B() };
         final String EXP = "[{\"BB\":{\"value\":2}}]";
 
         // first, with defaults
-        m = new ObjectMapper();
-        assertEquals(EXP, m.writeValueAsString(input));
+        assertEquals(EXP, MAPPER.writeValueAsString(input));
 
         // then with static typing enabled:
-        m = new ObjectMapper();
+        ObjectMapper m = new ObjectMapper();
         m.configure(MapperFeature.USE_STATIC_TYPING, true);
         assertEquals(EXP, m.writeValueAsString(input));
     }
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedContainerSerialization.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedContainerSerialization.java
index 92cc507..5c3bd75 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedContainerSerialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedContainerSerialization.java
@@ -9,12 +9,13 @@
 import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
 import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
 import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
+
 import com.fasterxml.jackson.core.type.TypeReference;
+
 import com.fasterxml.jackson.databind.BaseMapTest;
 import com.fasterxml.jackson.databind.JavaType;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-import com.fasterxml.jackson.databind.type.TypeFactory;
 
 public class TestTypedContainerSerialization
 	extends BaseMapTest
@@ -123,13 +124,13 @@
 
     public void testIssue329() throws Exception
     {
-            ArrayList<Animal> animals = new ArrayList<Animal>();
-            animals.add(new Dog("Spot"));
-            JavaType rootType = TypeFactory.defaultInstance().constructParametrizedType(Iterator.class, Iterator.class, Animal.class);
-            String json = mapper.writerFor(rootType).writeValueAsString(animals.iterator());
-            if (json.indexOf("\"object-type\":\"doggy\"") < 0) {
-                fail("No polymorphic type retained, should be; JSON = '"+json+"'");
-            }
+        ArrayList<Animal> animals = new ArrayList<Animal>();
+        animals.add(new Dog("Spot"));
+        JavaType rootType = mapper.getTypeFactory().constructParametricType(Iterator.class, Animal.class);
+        String json = mapper.writerFor(rootType).writeValueAsString(animals.iterator());
+        if (json.indexOf("\"object-type\":\"doggy\"") < 0) {
+            fail("No polymorphic type retained, should be; JSON = '"+json+"'");
+        }
     }
 
     public void testIssue508() throws Exception
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestVisibleTypeId.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestVisibleTypeId.java
index 3b9003e..3e7d317 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestVisibleTypeId.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestVisibleTypeId.java
@@ -6,7 +6,7 @@
 import com.fasterxml.jackson.databind.*;
 
 /**
- * Tests to verify [JACKSON-437], [JACKSON-762]
+ * Tests to verify that Type Id may be exposed during deserialization,
  */
 public class TestVisibleTypeId extends BaseMapTest
 {
@@ -92,7 +92,7 @@
     static class ExternalIdBean2 {
         public int a = 2;
 
-        /* Type id property itself can not be external, as it is conceptually
+        /* Type id property itself cannot be external, as it is conceptually
          * part of the bean for which info is written:
          */
         @JsonTypeId
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestWithGenerics.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestWithGenerics.java
index b3529f0..bb4cd9b 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestWithGenerics.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestWithGenerics.java
@@ -156,7 +156,8 @@
     {
         Dog dog = new Dog("Fluffy", 3);
         ContainerWithGetter<Animal> c2 = new ContainerWithGetter<Animal>(dog);
-        String json = MAPPER.writerFor(MAPPER.getTypeFactory().constructParametrizedType(ContainerWithGetter.class, ContainerWithGetter.class, Animal.class)).writeValueAsString(c2);
+        String json = MAPPER.writerFor(MAPPER.getTypeFactory().constructParametricType(ContainerWithGetter.class,
+                Animal.class)).writeValueAsString(c2);
         if (json.indexOf("\"object-type\":\"doggy\"") < 0) {
             fail("polymorphic type not kept, result == "+json+"; should contain 'object-type':'...'");
         }
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TypeDeserializerTest.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TypeDeserializerTest.java
new file mode 100644
index 0000000..2b1fa8e
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TypeDeserializerTest.java
@@ -0,0 +1,43 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.*;
+
+public class TypeDeserializerTest extends BaseMapTest
+{
+    public void testUtilMethods() throws Exception
+    {
+        final JsonFactory f = new JsonFactory();
+
+        JsonParser p = f.createParser("true");
+        assertNull(TypeDeserializer.deserializeIfNatural(p, null, Object.class));
+        p.nextToken();
+        assertEquals(Boolean.TRUE, TypeDeserializer.deserializeIfNatural(p, null, Object.class));
+        p.close();
+
+        p = f.createParser("false ");
+        p.nextToken();
+        assertEquals(Boolean.FALSE, TypeDeserializer.deserializeIfNatural(p, null, Object.class));
+        p.close();
+
+        p = f.createParser("1");
+        p.nextToken();
+        assertEquals(Integer.valueOf(1), TypeDeserializer.deserializeIfNatural(p, null, Object.class));
+        p.close();
+
+        p = f.createParser("0.5 ");
+        p.nextToken();
+        assertEquals(Double.valueOf(0.5), TypeDeserializer.deserializeIfNatural(p, null, Object.class));
+        p.close();
+
+        p = f.createParser("\"foo\" [ ] ");
+        p.nextToken();
+        assertEquals("foo", TypeDeserializer.deserializeIfNatural(p, null, Object.class));
+
+        p.nextToken();
+        assertNull(TypeDeserializer.deserializeIfNatural(p, null, Object.class));
+
+        p.close();
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForArrays.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForArrays.java
similarity index 74%
rename from src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForArrays.java
rename to src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForArrays.java
index 154630d..cd6d34e 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForArrays.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForArrays.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.jsontype;
+package com.fasterxml.jackson.databind.jsontype.deftyping;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -23,7 +23,15 @@
         public ArrayBean() { this(null); }
         public ArrayBean(Object[] v) { values = v; }
     }
-    
+
+    static class PrimitiveArrayBean {
+        @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
+        public Object stuff;
+
+        protected PrimitiveArrayBean() { }
+        public PrimitiveArrayBean(Object value) { stuff = value; }
+    }
+
     /*
     /**********************************************************
     /* Unit tests
@@ -57,7 +65,6 @@
         assertEquals(String[][].class, result.values.getClass());
     }
 
-    // @since 1.8
     public void testNodeInArray() throws Exception
     {
         JsonNode node = new ObjectMapper().readTree("{\"a\":3}");
@@ -72,6 +79,7 @@
         assertTrue(ob instanceof JsonNode);
     }
     
+    @SuppressWarnings("deprecation")
     public void testNodeInEmptyArray() throws Exception {
         Map<String, List<String>> outerMap = new HashMap<String, List<String>>();
         outerMap.put("inner", new ArrayList<String>());
@@ -90,7 +98,6 @@
         assertEquals("{}", result[0].toString());
     }
 
-    // test for [JACKSON-845]
     public void testArraysOfArrays() throws Exception
     {
         ObjectMapper mapper = new ObjectMapper();
@@ -105,6 +112,29 @@
         _testArraysAs(mapper, json, Object.class);
     }
 
+    public void testArrayTypingForPrimitiveArrays() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.enableDefaultTyping(DefaultTyping.NON_CONCRETE_AND_ARRAYS);
+        _testArrayTypingForPrimitiveArrays(m, new int[] { 1, 2, 3 });
+        _testArrayTypingForPrimitiveArrays(m, new long[] { 1, 2, 3 });
+        _testArrayTypingForPrimitiveArrays(m, new short[] { 1, 2, 3 });
+        _testArrayTypingForPrimitiveArrays(m, new double[] { 0.5, 5.5, -1.0 });
+        _testArrayTypingForPrimitiveArrays(m, new float[] { 0.5f, 5.5f, -1.0f });
+        _testArrayTypingForPrimitiveArrays(m, new boolean[] { true, false });
+        _testArrayTypingForPrimitiveArrays(m, new byte[] { 1, 2, 3 });
+
+        _testArrayTypingForPrimitiveArrays(m, new char[] { 'a', 'b' });
+    }
+
+    private void _testArrayTypingForPrimitiveArrays(ObjectMapper mapper, Object v) throws Exception {
+        PrimitiveArrayBean input = new PrimitiveArrayBean(v);
+        String json = mapper.writeValueAsString(input);
+        PrimitiveArrayBean result = mapper.readValue(json, PrimitiveArrayBean.class);
+        assertNotNull(result.stuff);
+        assertSame(v.getClass(), result.stuff.getClass());
+    }
+
     /*
     /**********************************************************
     /* Helper methods
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForEnums.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForEnums.java
similarity index 92%
rename from src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForEnums.java
rename to src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForEnums.java
index 645530c..b716c24 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForEnums.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForEnums.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.jsontype;
+package com.fasterxml.jackson.databind.jsontype.deftyping;
 
 import java.util.concurrent.TimeUnit;
 
@@ -23,7 +23,7 @@
     protected static class TimeUnitBean {
         public TimeUnit timeUnit;
     }
-    
+
     /*
     /**********************************************************
     /* Test methods
@@ -57,7 +57,7 @@
         
         // Typing is needed for enums
         String json = m.writeValueAsString(new Object[] { TestEnum.A });
-        assertEquals("[[\"com.fasterxml.jackson.databind.jsontype.TestDefaultForEnums$TestEnum\",\"A\"]]", json);
+        assertEquals("[[\"com.fasterxml.jackson.databind.jsontype.deftyping.TestDefaultForEnums$TestEnum\",\"A\"]]", json);
 
         // and let's verify we get it back ok as well:
         Object[] value = m.readValue(json, Object[].class);
@@ -70,7 +70,7 @@
         ObjectMapper m = new ObjectMapper();
         m.enableDefaultTyping();
         String json = m.writeValueAsString(new EnumHolder(TestEnum.B));
-        assertEquals("{\"value\":[\"com.fasterxml.jackson.databind.jsontype.TestDefaultForEnums$TestEnum\",\"B\"]}", json);
+        assertEquals("{\"value\":[\"com.fasterxml.jackson.databind.jsontype.deftyping.TestDefaultForEnums$TestEnum\",\"B\"]}", json);
         EnumHolder holder = m.readValue(json, EnumHolder.class);
         assertSame(TestEnum.B, holder.value);
     }
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForLists.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForLists.java
similarity index 98%
rename from src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForLists.java
rename to src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForLists.java
index df55697..b061be6 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForLists.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForLists.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.jsontype;
+package com.fasterxml.jackson.databind.jsontype.deftyping;
 
 import java.util.*;
 
@@ -115,7 +115,7 @@
         inputList.add(Locale.CHINESE);
         input.values = inputList;
         String json = m.writeValueAsString(input);
-        
+
         ObjectListBean output = m.readValue(json, ObjectListBean.class);
         List<Object> outputList = output.values;
         assertEquals(2, outputList.size());
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForMaps.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForMaps.java
similarity index 98%
rename from src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForMaps.java
rename to src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForMaps.java
index c36ebd2..c1fca0a 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForMaps.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForMaps.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.jsontype;
+package com.fasterxml.jackson.databind.jsontype.deftyping;
 
 import java.util.*;
 
@@ -118,8 +118,6 @@
                 TypeFactory.defaultInstance().constructType(Object.class), subtypes, forSerialization, !forSerialization);
     }
 
-    // // For #234:
-    
     public void testList() throws Exception
     {
         final ObjectMapper mapper = new ObjectMapper();
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForObject.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForObject.java
similarity index 97%
rename from src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForObject.java
rename to src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForObject.java
index 081af8a..5f0a23a 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForObject.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForObject.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.jsontype;
+package com.fasterxml.jackson.databind.jsontype.deftyping;
 
 import java.util.*;
 
@@ -136,7 +136,7 @@
             fail("Should have failed");
         } catch (JsonMappingException e) {
             // let's use whatever is currently thrown exception... may change tho
-            verifyException(e, "can not construct");
+            verifyException(e, "cannot construct");
         }
         
         // and then that we will succeed with default type info
@@ -337,7 +337,7 @@
         ObjectMapper mapper = new ObjectMapper();
         mapper.enableDefaultTypingAsProperty(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE, "*CLASS*");
         String json = mapper.writeValueAsString(new BeanHolder(new StringBean("punny")));
-        assertEquals("{\"bean\":{\"*CLASS*\":\"com.fasterxml.jackson.databind.jsontype.TestDefaultForObject$StringBean\",\"name\":\"punny\"}}", json);
+        assertEquals("{\"bean\":{\"*CLASS*\":\"com.fasterxml.jackson.databind.jsontype.deftyping.TestDefaultForObject$StringBean\",\"name\":\"punny\"}}", json);
     }
 
     public void testNoGoWithExternalProperty() throws Exception
@@ -348,7 +348,7 @@
                     JsonTypeInfo.As.EXTERNAL_PROPERTY);
             fail("Should not have passed");
         } catch (IllegalArgumentException e) {
-            verifyException(e, "Can not use includeAs of EXTERNAL_PROPERTY");
+            verifyException(e, "Cannot use includeAs of EXTERNAL_PROPERTY");
         }
     }
     
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForScalars.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForScalars.java
similarity index 73%
rename from src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForScalars.java
rename to src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForScalars.java
index c2a14fb..7bb7b5c 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForScalars.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForScalars.java
@@ -1,10 +1,12 @@
-package com.fasterxml.jackson.databind.jsontype;
+package com.fasterxml.jackson.databind.jsontype.deftyping;
 
 import java.util.*;
 
 import static org.junit.Assert.*;
 
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
 import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder;
 
 /**
  * Unit tests to verify that Java/JSON scalar values (non-structured values)
@@ -18,9 +20,14 @@
         public java.io.Serializable bar = new Integer(13);
     }
 
+    // [databind#1395]: prevent attempts at including type info for primitives
+    static class Data {
+        public long key;
+    }
+
     /*
     /**********************************************************
-    /* Unit tests
+    /* Test methods
     /**********************************************************
      */
     
@@ -92,9 +99,6 @@
         assertArrayEquals(input, output);
     }
 
-    /**
-     * Loosely scalar; for [JACKSON-417]
-     */
     public void test417() throws Exception
     {
         ObjectMapper m = new ObjectMapper();
@@ -105,4 +109,31 @@
         assertEquals(input.foo, result.foo);
         assertEquals(input.bar, result.bar);
     }
+
+    // [databind#1395]: prevent attempts at including type info for primitives
+    public void testDefaultTypingWithLong() throws Exception
+    {
+        Data data = new Data();
+        data.key = 1L;
+        Map<String, Object> mapData = new HashMap<String, Object>();
+        mapData.put("longInMap", 2L);
+        mapData.put("longAsField", data);
+
+        // Configure Jackson to preserve types
+        ObjectMapper mapper = new ObjectMapper();
+        StdTypeResolverBuilder resolver = new StdTypeResolverBuilder();
+        resolver.init(JsonTypeInfo.Id.CLASS, null);
+        resolver.inclusion(JsonTypeInfo.As.PROPERTY);
+        resolver.typeProperty("__t");
+        mapper.setDefaultTyping(resolver);
+        mapper.enable(SerializationFeature.INDENT_OUTPUT);
+
+        // Serialize
+        String json = mapper.writeValueAsString(mapData);
+
+        // Deserialize
+        Map<?,?> result = mapper.readValue(json, Map.class);
+        assertNotNull(result);
+        assertEquals(2, result.size());
+    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForTreeNodes.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForTreeNodes.java
similarity index 95%
rename from src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForTreeNodes.java
rename to src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForTreeNodes.java
index 83876db..39a2cee 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForTreeNodes.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForTreeNodes.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.jsontype;
+package com.fasterxml.jackson.databind.jsontype.deftyping;
 
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
 import com.fasterxml.jackson.databind.*;
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultWithCreators.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultWithCreators.java
similarity index 97%
rename from src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultWithCreators.java
rename to src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultWithCreators.java
index 785a3b5..b75b478 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultWithCreators.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultWithCreators.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.jsontype;
+package com.fasterxml.jackson.databind.jsontype.deftyping;
 
 import org.junit.Assert;
 
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/ExternalTypeIdTest.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/ExternalTypeIdTest.java
index 8913f7a..7537129 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/ExternalTypeIdTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/ExternalTypeIdTest.java
@@ -294,10 +294,10 @@
         ObjectMapper mapper = new ObjectMapper();
         mapper.registerSubtypes(ValueBean.class);
         // This may look odd, but one implementation nastiness is the fact
-        // that we can not properly serialize type id before the object,
+        // that we cannot properly serialize type id before the object,
         // because call is made after property name (for object) has already
         // been written out. So we'll write it after...
-        // Deserializer will work either way as it can not rely on ordering
+        // Deserializer will work either way as it cannot rely on ordering
         // anyway.
         assertEquals("{\"bean\":{\"value\":11},\"extType\":\"vbean\"}",
                 mapper.writeValueAsString(new ExternalBean(11)));
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/ExternalTypeIdWithEnum1328Test.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/ExternalTypeIdWithEnum1328Test.java
new file mode 100644
index 0000000..0ce3a65
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/ExternalTypeIdWithEnum1328Test.java
@@ -0,0 +1,91 @@
+package com.fasterxml.jackson.databind.jsontype.ext;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonTypeIdResolver;
+import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
+
+public class ExternalTypeIdWithEnum1328Test extends BaseMapTest
+{
+    public interface Animal { }
+
+    public static class Dog implements Animal {
+        public String dogStuff;
+    }
+    
+    public enum AnimalType {
+        Dog;
+    }
+
+    public static class AnimalAndType {
+        public AnimalType type;
+
+        @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, 
+            include = JsonTypeInfo.As.EXTERNAL_PROPERTY, 
+            property = "type")
+        @JsonTypeIdResolver(AnimalResolver.class)   
+        private Animal animal;
+
+        public AnimalAndType() { }
+
+        // problem is this annotation
+        @java.beans.ConstructorProperties({"type", "animal"})
+        public AnimalAndType(final AnimalType type, final Animal animal) {
+            this.type = type;
+            this.animal = animal;
+        }
+    }
+
+    static class AnimalResolver implements TypeIdResolver {
+        @Override
+        public void init(JavaType bt) { }
+
+        @Override
+        public String idFromValue(Object value) {
+            return null;
+        }
+
+        @Override
+        public String idFromValueAndType(Object value, Class<?> suggestedType) {
+            return null;
+        }
+
+        @Override
+        public String idFromBaseType() {
+            throw new UnsupportedOperationException("Missing action type information - Can not construct");
+        }
+
+        @Override
+        public JavaType typeFromId(DatabindContext context, String id) throws IOException {
+            if (AnimalType.Dog.toString().equals(id)) {
+                return context.constructType(Dog.class);
+            }
+            throw new IllegalArgumentException("What is a " + id);
+        }
+
+        @Override
+        public String getDescForKnownTypeIds() {
+            return null;
+        }
+
+        @Override
+        public JsonTypeInfo.Id getMechanism() {
+            return JsonTypeInfo.Id.CUSTOM;
+        } 
+    }
+
+    public void testExample() throws Exception {
+        ObjectMapper mapper = new ObjectMapper();
+        
+        String json = mapper.writerWithDefaultPrettyPrinter()
+                .writeValueAsString(Arrays.asList(new AnimalAndType(AnimalType.Dog, new Dog())));
+        List<AnimalAndType> list = mapper.readerFor(new TypeReference<List<AnimalAndType>>() { })
+            .readValue(json);
+        assertNotNull(list);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/MultipleExternalIds291Test.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/MultipleExternalIds291Test.java
new file mode 100644
index 0000000..b015dd8
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/ext/MultipleExternalIds291Test.java
@@ -0,0 +1,111 @@
+package com.fasterxml.jackson.databind.jsontype.ext;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
+import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
+import com.fasterxml.jackson.databind.*;
+
+public class MultipleExternalIds291Test extends BaseMapTest
+{
+    // For [Issue#291]
+    interface F1 {}
+
+    static class A implements F1 {
+        public String a;
+    }
+
+    static class B implements F1 {
+        public String b;
+    }
+
+    static interface F2 {}
+
+    static class C implements F2 {
+        public String c;
+    }
+
+    static class D implements F2{
+        public String d;
+    }
+
+    static class Container {
+        @JsonTypeInfo(use = Id.NAME, property = "type", include = As.EXTERNAL_PROPERTY)
+        @JsonSubTypes({
+                @JsonSubTypes.Type(value = A.class, name = "1"),
+                @JsonSubTypes.Type(value = B.class, name = "2")})
+        public F1 field1;
+
+        @JsonTypeInfo(use = Id.NAME, property = "type", include = As.EXTERNAL_PROPERTY)
+        @JsonSubTypes({
+                @JsonSubTypes.Type(value = C.class, name = "1"),
+                @JsonSubTypes.Type(value = D.class, name = "2")})
+        public F2 field2;
+    }
+
+    static class ContainerWithExtra extends Container {
+        public String type;
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    final ObjectMapper MAPPER = objectMapper();
+    
+    // [databind#291]
+    public void testMultipleValuesSingleExtId() throws Exception
+    {
+        // first with ext-id before values
+        _testMultipleValuesSingleExtId(
+"{'type' : '1',\n"
++"'field1' : { 'a' : 'AAA' },\n"
++"'field2' : { 'c' : 'CCC' }\n"
++"}"
+);
+
+        // then after
+        _testMultipleValuesSingleExtId(
+"{\n"
++"'field1' : { 'a' : 'AAA' },\n"
++"'field2' : { 'c' : 'CCC' },\n"
++"'type' : '1'\n"
++"}"
+);
+        // and then in-between
+        _testMultipleValuesSingleExtId(
+"{\n"
++"'field1' : { 'a' : 'AAA' },\n"
++"'type' : '1',\n"
++"'field2' : { 'c' : 'CCC' }\n"
++"}"
+);
+    }
+
+    public void _testMultipleValuesSingleExtId(String json) throws Exception
+    {
+        json = aposToQuotes(json);
+
+        // First, with base class, no type id field separately
+        {
+            Container c = MAPPER.readValue(json, Container.class);
+            assertNotNull(c);
+            assertTrue(c.field1 instanceof A);
+            assertEquals("AAA", ((A) c.field1).a);
+            assertTrue(c.field2 instanceof C);
+            assertEquals("CCC", ((C) c.field2).c);
+        }
+
+        // then with sub-class that does have similarly named property
+        {
+            ContainerWithExtra c = MAPPER.readValue(json, ContainerWithExtra.class);
+            assertNotNull(c);
+            assertEquals("1", c.type);
+            assertTrue(c.field1 instanceof A);
+            assertEquals("AAA", ((A) c.field1).a);
+            assertTrue(c.field2 instanceof C);
+            assertEquals("CCC", ((C) c.field2).c);
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/misc/AccessFixTest.java b/src/test/java/com/fasterxml/jackson/databind/misc/AccessFixTest.java
index 64c4ca5..c53b9d3 100644
--- a/src/test/java/com/fasterxml/jackson/databind/misc/AccessFixTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/misc/AccessFixTest.java
@@ -14,7 +14,7 @@
         @Override
         public void checkPermission(Permission perm) throws SecurityException {
             if ("suppressAccessChecks".equals(perm.getName())) {
-                throw new SecurityException("Can not force permission: "+perm);
+                throw new SecurityException("Cannot force permission: "+perm);
             }
         }
     }
diff --git a/src/test/java/com/fasterxml/jackson/databind/misc/BeanPropertyMapTest.java b/src/test/java/com/fasterxml/jackson/databind/misc/BeanPropertyMapTest.java
index eafa286..2240d5a 100644
--- a/src/test/java/com/fasterxml/jackson/databind/misc/BeanPropertyMapTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/misc/BeanPropertyMapTest.java
@@ -31,7 +31,8 @@
         PropertyMetadata md = PropertyMetadata.STD_REQUIRED;
         props.add(new ObjectIdValueProperty(new MyObjectIdReader("pk"), md));
         props.add(new ObjectIdValueProperty(new MyObjectIdReader("firstName"), md));
-        BeanPropertyMap propMap = new BeanPropertyMap(false, props);
+        BeanPropertyMap propMap = new BeanPropertyMap(false, props,
+                new HashMap<String,List<PropertyName>>());
         propMap = propMap.withProperty(new ObjectIdValueProperty(new MyObjectIdReader("@id"), md));
         assertNotNull(propMap);
     }
diff --git a/src/test/java/com/fasterxml/jackson/databind/misc/CaseInsensitive1854Test.java b/src/test/java/com/fasterxml/jackson/databind/misc/CaseInsensitive1854Test.java
new file mode 100644
index 0000000..10f5449
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/misc/CaseInsensitive1854Test.java
@@ -0,0 +1,67 @@
+package com.fasterxml.jackson.databind.misc;
+
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.databind.*;
+
+public class CaseInsensitive1854Test extends BaseMapTest
+{
+    static class Obj1854 {
+        private final int id;
+
+// 17-Dec-2017, tatu: One of following would work around the bug [databind#1854]        
+//        @JsonProperty("Items")
+//        @JsonIgnore
+        private final List<ChildObj> items;
+
+        public Obj1854(int id, List<ChildObj> items) {
+            this.id = id;
+            this.items = items;
+        }
+
+        @JsonCreator
+        public static Obj1854 fromJson(@JsonProperty("ID") int id,
+                @JsonProperty("Items") List<ChildObj> items) {
+            return new Obj1854(id, items);
+        }
+
+        public int getId() {
+            return id;
+        }
+
+        public List<ChildObj> getItems() {
+            return items;
+        }
+
+    }
+
+    static class ChildObj {
+        private final String childId;
+
+        private ChildObj(String id) {
+            this.childId = id;
+        }
+
+        @JsonCreator
+        public static ChildObj fromJson(@JsonProperty("ChildID") String cid) {
+            return new ChildObj(cid);
+        }
+
+        public String getId() {
+            return childId;
+        }
+    }
+
+    public void testIssue1854() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);
+        final String DOC = aposToQuotes("{'ID': 1, 'Items': [ { 'ChildID': 10 } ]}");
+        Obj1854 result = mapper.readValue(DOC, Obj1854.class);
+        assertNotNull(result);
+        assertEquals(1, result.getId());
+        assertNotNull(result.getItems());
+        assertEquals(1, result.getItems().size());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/misc/CaseInsensitiveDeserTest.java b/src/test/java/com/fasterxml/jackson/databind/misc/CaseInsensitiveDeserTest.java
index 66b9554..9ae53ca 100644
--- a/src/test/java/com/fasterxml/jackson/databind/misc/CaseInsensitiveDeserTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/misc/CaseInsensitiveDeserTest.java
@@ -1,6 +1,7 @@
 package com.fasterxml.jackson.databind.misc;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.*;
@@ -20,6 +21,18 @@
         public String name, value;
     }
 
+    // [databind#1232]: allow per-property case-insensitivity
+    static class Role {
+        public String ID;
+        public String Name;
+    }
+
+    static class CaseInsensitiveRoleWrapper
+    {
+        @JsonFormat(with={ JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES })
+        public Role role;
+    }
+
     // [databind#1438]
     static class InsensitiveCreator
     {
@@ -37,6 +50,7 @@
     /********************************************************
      */
 
+    private final ObjectMapper MAPPER = new ObjectMapper();
     private final ObjectMapper INSENSITIVE_MAPPER = new ObjectMapper();
     {
         INSENSITIVE_MAPPER.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);
@@ -78,6 +92,16 @@
         assertEquals("Signature not valid!", response.debugMessage);
     }
 
+    // [databind#1232]: allow per-property case-insensitivity
+    public void testCaseInsensitiveWithFormat() throws Exception {
+        CaseInsensitiveRoleWrapper w = MAPPER.readValue
+                (aposToQuotes("{'role':{'id':'12','name':'Foo'}}"),
+                        CaseInsensitiveRoleWrapper.class);
+        assertNotNull(w);
+        assertEquals("12", w.role.ID);
+        assertEquals("Foo", w.role.Name);
+    }
+    
     // [databind#1438]
     public void testCreatorWithInsensitive() throws Exception
     {
@@ -85,4 +109,19 @@
         InsensitiveCreator bean = INSENSITIVE_MAPPER.readValue(json, InsensitiveCreator.class);
         assertEquals(3, bean.v);
     }
+
+    // And allow config overrides too
+    public void testCaseInsensitiveWithClassFormat() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.configOverride(Role.class)
+            .setFormat(JsonFormat.Value.empty()
+                    .withFeature(JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES));
+        Role role = mapper.readValue
+                (aposToQuotes("{'id':'12','name':'Foo'}"),
+                        Role.class);
+        assertNotNull(role);
+        assertEquals("12", role.ID);
+        assertEquals("Foo", role.Name);
+    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/misc/RaceCondition738Test.java b/src/test/java/com/fasterxml/jackson/databind/misc/RaceCondition738Test.java
index 9168b25..bb5bb34 100644
--- a/src/test/java/com/fasterxml/jackson/databind/misc/RaceCondition738Test.java
+++ b/src/test/java/com/fasterxml/jackson/databind/misc/RaceCondition738Test.java
@@ -56,14 +56,14 @@
      */
     
     public void testRepeatedly() throws Exception {
-        final int COUNT = 2000;
+        final int COUNT = 3000;
         for (int i = 0; i < COUNT; i++) {
             runOnce(i, COUNT);
         }
     }
     
     void runOnce(int round, int max) throws Exception {
-        final ObjectMapper mapper = getObjectMapper();
+        final ObjectMapper mapper = newObjectMapper();
         Callable<String> writeJson = new Callable<String>() {
             @Override
             public String call() throws Exception {
@@ -92,8 +92,4 @@
             }
         }
     }
-
-    private static ObjectMapper getObjectMapper() {
-        return new ObjectMapper();
-    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/misc/ThreadSafety1759Test.java b/src/test/java/com/fasterxml/jackson/databind/misc/ThreadSafety1759Test.java
new file mode 100644
index 0000000..236629f
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/misc/ThreadSafety1759Test.java
@@ -0,0 +1,71 @@
+package com.fasterxml.jackson.databind.misc;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.fasterxml.jackson.databind.*;
+
+public class ThreadSafety1759Test extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    public void testCalendarForDeser() throws Exception
+    {
+        final ObjectMapper mapper = newObjectMapper();
+
+        final int numThreads = 4;
+        final int COUNT = 3000;
+        final AtomicInteger counter = new AtomicInteger();
+
+        // IMPORTANT! Use different timestamp for every thread
+        List<Callable<Throwable>> calls = new ArrayList<Callable<Throwable>>();
+        for (int thread = 1; thread <= numThreads; ++thread) {
+            final String json = quote(String.format("2017-01-%02dT16:30:49Z", thread));
+            final long timestamp = mapper.readValue(json, Date.class).getTime();
+
+            calls.add(createCallable(thread, mapper, json, timestamp, COUNT, counter));
+        }
+
+        ExecutorService executor = Executors.newFixedThreadPool(numThreads);
+        List<Future<Throwable>> results = new ArrayList<>();
+        for (Callable<Throwable> c : calls) {
+            results.add(executor.submit(c));
+        }
+        executor.shutdown();
+        for (Future<Throwable> f : results) {
+            Throwable t = f.get(5, TimeUnit.SECONDS);
+            if (t != null) {
+                fail("Exception during processing: "+t.getMessage());
+            }
+        }
+        assertEquals(numThreads * COUNT, counter.get());
+    }
+
+    private Callable<Throwable> createCallable(final int threadId,
+            final ObjectMapper mapper,
+            final String json, final long timestamp,
+            final int count, final AtomicInteger counter)
+    {
+        return new Callable<Throwable>() {
+            @Override
+            public Throwable call() throws IOException {
+                for (int i = 0; i < count; ++i) {
+                    Date dt = mapper.readValue(json, Date.class);
+                    if (dt.getTime() != timestamp) {
+                        return new IllegalArgumentException("Wrong timestamp (thread id "+threadId+", input: "+json+": should get "+timestamp+", got "+dt.getTime());
+                    }
+                    counter.addAndGet(1);
+                }
+                return null;
+            }
+        };
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/mixins/MapperMixinsCopy1998Test.java b/src/test/java/com/fasterxml/jackson/databind/mixins/MapperMixinsCopy1998Test.java
new file mode 100644
index 0000000..32897e6
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/mixins/MapperMixinsCopy1998Test.java
@@ -0,0 +1,127 @@
+package com.fasterxml.jackson.databind.mixins;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+
+public class MapperMixinsCopy1998Test extends BaseMapTest
+{
+    final static String FULLMODEL="{\"format\":\"1.0\",\"child\":{\"type\":\"CHILD_B\",\"name\":\"testB\"},\"notVisible\":\"should not be present\"}";
+    final static String EXPECTED="{\"format\":\"1.0\",\"child\":{\"name\":\"testB\"}}";
+
+    static class MyModelView { }
+
+    interface MixinConfig {
+        interface MyModelRoot {
+            @JsonView(MyModelView.class)
+            public String getFormat();
+
+            @JsonView(MyModelView.class)
+            public MyModelChildBase getChild();
+        }
+
+        @JsonTypeInfo(use = JsonTypeInfo.Id.NONE, include = JsonTypeInfo.As.EXISTING_PROPERTY)
+        interface MyModelChildBase {
+            @JsonView(MyModelView.class)
+            public String getName();
+        }
+
+    }
+
+    @JsonPropertyOrder({ "format", "child" })
+    static class MyModelRoot {
+        @JsonProperty
+        private String format = "1.0";
+
+        public String getFormat() {
+            return format;
+        }
+        @JsonProperty
+        private MyModelChildBase child;
+
+        public MyModelChildBase getChild() {
+            return child;
+        }
+
+        public void setChild(MyModelChildBase child) {
+            this.child = child;
+        }
+
+        @JsonProperty
+        private String notVisible = "should not be present";
+
+        public String getNotVisible() {
+            return notVisible;
+        }
+    }
+
+    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
+    @JsonSubTypes({
+            @JsonSubTypes.Type(value = MyChildA.class, name = "CHILD_A"),
+            @JsonSubTypes.Type(value = MyChildB.class, name = "CHILD_B")
+    })
+    abstract static class MyModelChildBase {
+        @JsonProperty
+        private String name;
+
+        public String getName() {
+            return name;
+        }
+
+        @JsonIgnore
+        public void setName(String name) {
+            this.name = name;
+        }
+    }
+
+    static class MyChildA extends MyModelChildBase {
+        public MyChildA(String name) {
+            setName(name);
+        }
+    }
+
+    static class MyChildB extends MyModelChildBase {
+        public MyChildB(String name) {
+            setName(name);
+        }
+    }
+
+    public void testB_KO() throws Exception
+    {
+        final ObjectMapper DEFAULT = defaultMapper();
+        MyModelRoot myModelInstance = new MyModelRoot();
+        myModelInstance.setChild(new MyChildB("testB"));
+
+        ObjectMapper myObjectMapper = DEFAULT.copy();
+
+        String postResult = getString(myModelInstance, myObjectMapper);
+        assertEquals(FULLMODEL, postResult);
+//        System.out.println("postResult: "+postResult);
+
+        myObjectMapper = DEFAULT.copy();
+//        myObjectMapper = defaultMapper();
+        myObjectMapper.addMixIn(MyModelRoot.class, MixinConfig.MyModelRoot.class)
+                .addMixIn(MyModelChildBase.class, MixinConfig.MyModelChildBase.class)
+                .disable(MapperFeature.DEFAULT_VIEW_INCLUSION)
+                .setConfig(myObjectMapper.getSerializationConfig().withView(MyModelView.class));
+
+        String result = getString(myModelInstance, myObjectMapper);
+        assertEquals(EXPECTED, result);
+
+    }
+
+    private String getString(MyModelRoot myModelInstance, ObjectMapper myObjectMapper) throws IOException {
+        return myObjectMapper.writerFor(MyModelRoot.class).writeValueAsString(myModelInstance);
+    }
+
+    private ObjectMapper defaultMapper()
+    {
+        return new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_EMPTY)
+                .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
+                .configure(MapperFeature.ALLOW_COERCION_OF_SCALARS, false)
+                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true)
+            ;
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForClass.java b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForClass.java
index d36132f..50e1d1c 100644
--- a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForClass.java
+++ b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForClass.java
@@ -10,12 +10,6 @@
 public class TestMixinDeserForClass
     extends BaseMapTest
 {
-    /*
-    /**********************************************************
-    /* Helper bean classes
-    /**********************************************************
-     */
-
     static class BaseClass
     {
         /* property that is always found; but has lower priority than
@@ -35,6 +29,21 @@
     @JsonAutoDetect(setterVisibility=Visibility.NONE, fieldVisibility=Visibility.NONE)
     interface MixIn { }
 
+    // [databind#1990]
+    public interface HashCodeMixIn {
+        @Override
+        @JsonProperty
+        public int hashCode();
+    }
+
+    public class Bean1990WithoutHashCode {
+    }
+
+    public class Bean1990WithHashCode {
+        @Override
+        public int hashCode() { return 13; }
+    }
+
     /*
     /**********************************************************
     /* Unit tests
@@ -48,18 +57,16 @@
         LeafClass result = m.readValue("{\"a\":\"value\"}", LeafClass.class);
         assertEquals("XXXvalue", result.a);
 
-        /* Then with leaf-level mix-in; without (method) auto-detect, should
-         * use field
-         */
+        // Then with leaf-level mix-in; without (method) auto-detect,
+        // should use field
         m = new ObjectMapper();
         m.addMixIn(LeafClass.class, MixIn.class);
         result = m.readValue("{\"a\":\"value\"}", LeafClass.class);
         assertEquals("value", result.a);
     }
 
-    /* and then a test for mid-level mixin; should have no effect
-     * when deserializing leaf (but will if deserializing base class)
-     */
+    // and then a test for mid-level mixin; should have no effect
+    // when deserializing leaf (but will if deserializing base class)
     public void testClassMixInsMidLevel() throws IOException
     {
         ObjectMapper m = new ObjectMapper();
@@ -95,4 +102,22 @@
             assertEquals("XXX", result.a);
         }
     }
+
+    // [databind#1990]: can apply mix-in to `Object#hashCode()` to force serialization
+    public void testHashCodeViaObject() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper()
+                .addMixIn(Object.class, HashCodeMixIn.class);
+
+        // First, with something that overrides hashCode()
+        assertEquals( "{\"hashCode\":13}",
+                mapper.writeValueAsString(new Bean1990WithHashCode()));
+
+        // and then special case of accessing Object#hashCode()
+        String prefix = "{\"hashCode\":";
+        String json = mapper.writeValueAsString(new Bean1990WithoutHashCode());
+        if (!json.startsWith(prefix)) {
+            fail("Should start with ["+prefix+"], does not: ["+json+"]");
+        }
+    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForCreators.java b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForCreators.java
index 5118711..22c2510 100644
--- a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForCreators.java
+++ b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForCreators.java
@@ -68,7 +68,7 @@
         private StringWrapper(String s, boolean foo) { _value = s; }
 
         @SuppressWarnings("unused")
-		private static StringWrapper create(String str) {
+        private static StringWrapper create(String str) {
             return new StringWrapper(str, false);
         }
     }
@@ -77,6 +77,32 @@
         @JsonCreator static StringWrapper create(String str) { return null; }
     }
 
+    // [databind#2020]
+    static class Pair2020 {
+        final int x, y;
+
+        private Pair2020(int x0, int y0) {
+            x = x0;
+            y = y0;
+        }
+
+        static Pair2020 with(Object x0, Object y0) {
+            return new Pair2020(((Number) x0).intValue(),
+                    ((Number) y0).intValue());
+        }
+    }
+
+    @JsonIgnoreProperties("size")
+    static class MyPairMixIn8 { // with and without <Long, String>
+         @JsonCreator
+         static TestMixinDeserForCreators.Pair2020 with(@JsonProperty("value0") Object value0,
+                   @JsonProperty("value1") Object value1)
+         {
+             // body does not matter, only signature
+             return null;
+         }
+    }
+
     /*
     /**********************************************************
     /* Unit tests
@@ -107,11 +133,23 @@
         assertEquals("stringX", result._a);
     }
 
-    public void testFactoryMixIn() throws IOException
+    public void testFactoryDelegateMixIn() throws IOException
     {
         ObjectMapper m = new ObjectMapper();
         m.addMixIn(StringWrapper.class, StringWrapperMixIn.class);
         StringWrapper result = m.readValue("\"a\"", StringWrapper.class);
         assertEquals("a", result._value);
     }
+
+    // [databind#2020]
+    public void testFactoryPropertyMixin() throws Exception
+    {
+        ObjectMapper objectMapper = new ObjectMapper();
+        objectMapper.addMixIn(Pair2020.class, MyPairMixIn8.class);
+
+        String doc = aposToQuotes( "{'value0' : 456, 'value1' : 789}");
+        Pair2020 pair2 = objectMapper.readValue(doc, Pair2020.class);
+        assertEquals(456, pair2.x);
+        assertEquals(789, pair2.y);
+    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/TestMixinMerging.java b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinMerging.java
similarity index 96%
rename from src/test/java/com/fasterxml/jackson/databind/introspect/TestMixinMerging.java
rename to src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinMerging.java
index 2595500..76230de 100644
--- a/src/test/java/com/fasterxml/jackson/databind/introspect/TestMixinMerging.java
+++ b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinMerging.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.introspect;
+package com.fasterxml.jackson.databind.mixins;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
 
diff --git a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerForMethods.java b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerForMethods.java
index 3ab6ce1..a57cffe 100644
--- a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerForMethods.java
+++ b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerForMethods.java
@@ -62,13 +62,6 @@
         @JsonIgnore
         public String takeB() { return null; }
     }
-               
-    interface ObjectMixIn
-    {
-        // and then ditto for hashCode..
-        @Override
-        @JsonProperty public int hashCode();
-    }
 
     static class EmptyBean { }
 
@@ -154,40 +147,6 @@
         assertEquals(Integer.valueOf(42), result.get("x"));
     }
 
-    /**
-     * Unit test for verifying that it is actually possible to attach
-     * mix-in annotations to basic <code>Object.class</code>. This
-     * will essentially apply to any and all Objects.
-     */
-    public void testObjectMixin() throws IOException
-    {
-        ObjectMapper mapper = new ObjectMapper();
-        mapper.addMixIn(Object.class, ObjectMixIn.class);
-
-        // First, with our bean...
-        Map<String,Object> result = writeAndMap(mapper, new BaseClass("a", "b"));
-
-        assertEquals(2, result.size());
-        assertEquals("b", result.get("b"));
-        Object ob = result.get("hashCode");
-        assertNotNull(ob);
-        assertEquals(Integer.class, ob.getClass());
-
-        /* 15-Oct-2010, tatu: Actually, we now block serialization (attempts) of plain Objects, by default
-         *    (since generally that makes no sense -- may need to revisit). As such, need to comment out
-         *    this part of test
-         */
-        /* Hmmh. For plain Object.class... I suppose getClass() does
-         * get serialized (and can't really be blocked either).
-         * Fine.
-         */
-        result = writeAndMap(mapper, new BaseClass("a", "b"));
-        assertEquals(2, result.size());
-        ob = result.get("hashCode");
-        assertNotNull(ob);
-        assertEquals(Integer.class, ob.getClass());
-    }
-
     // [databind#688]
     public void testCustomResolver() throws IOException
     {
@@ -195,8 +154,8 @@
         mapper.setMixInResolver(new ClassIntrospector.MixInResolver() {
             @Override
             public Class<?> findMixInClassFor(Class<?> target) {
-                if (target == BaseClass.class) {
-                    return ObjectMixIn.class;
+                if (target == EmptyBean.class) {
+                    return MixInForSimple.class;
                 }
                 return null;
             }
@@ -206,10 +165,8 @@
                 return this;
             }
         });
-        Map<String,Object> result = writeAndMap(mapper, new BaseClass("c", "d"));
-        assertEquals(2, result.size());
-        assertNotNull(result.get("hashCode"));
-        assertTrue(result.containsKey("b"));
-        assertFalse(result.containsKey("a"));
+        Map<String,Object> result = writeAndMap(mapper, new SimpleBean());
+        assertEquals(1, result.size());
+        assertEquals(Integer.valueOf(42), result.get("x"));
     }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/module/SimpleModuleArgCheckTest.java b/src/test/java/com/fasterxml/jackson/databind/module/SimpleModuleArgCheckTest.java
new file mode 100644
index 0000000..8de27cd
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/module/SimpleModuleArgCheckTest.java
@@ -0,0 +1,154 @@
+package com.fasterxml.jackson.databind.module;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import com.fasterxml.jackson.core.Version;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.jsontype.NamedType;
+
+public class SimpleModuleArgCheckTest extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Unit tests for invalid deserializers
+    /**********************************************************
+     */
+
+    public void testInvalidForDeserializers() throws Exception
+    {
+        SimpleModule mod = new SimpleModule("test", Version.unknownVersion(),
+                (Map<Class<?>,JsonDeserializer<?>>) null);
+
+        try {
+            mod.addDeserializer(String.class, null);
+            fail("Should not pass");
+        } catch (IllegalArgumentException e) {
+            verifyException(e, "Cannot pass `null` as deserializer");
+        }
+
+        try {
+            mod.addKeyDeserializer(String.class, null);
+            fail("Should not pass");
+        } catch (IllegalArgumentException e) {
+            verifyException(e, "Cannot pass `null` as key deserializer");
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests for invalid serializers
+    /**********************************************************
+     */
+
+    public void testInvalidForSerializers() throws Exception
+    {
+        SimpleModule mod = new SimpleModule("test", Version.unknownVersion(),
+                (List<JsonSerializer<?>>) null);
+
+        try {
+            mod.addSerializer(String.class, null);
+            fail("Should not pass");
+        } catch (IllegalArgumentException e) {
+            verifyException(e, "Cannot pass `null` as serializer");
+        }
+
+        try {
+            mod.addSerializer((JsonSerializer<?>) null);
+            fail("Should not pass");
+        } catch (IllegalArgumentException e) {
+            verifyException(e, "Cannot pass `null` as serializer");
+        }
+        
+        try {
+            mod.addKeySerializer(String.class, null);
+            fail("Should not pass");
+        } catch (IllegalArgumentException e) {
+            verifyException(e, "Cannot pass `null` as key serializer");
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests for invalid misc other
+    /**********************************************************
+     */
+
+    public void testInvalidAbstractTypeMapping() throws Exception
+    {
+        // just for funsies let's use more esoteric constructor
+        Map<Class<?>,JsonDeserializer<?>>  desers = Collections.emptyMap();
+        List<JsonSerializer<?>> sers = Collections.emptyList();
+        SimpleModule mod = new SimpleModule("test", Version.unknownVersion(),
+                desers, sers);
+
+        try {
+            mod.addAbstractTypeMapping(null, String.class);
+            fail("Should not pass");
+        } catch (IllegalArgumentException e) {
+            verifyException(e, "Cannot pass `null` as abstract type to map");
+        }
+        try {
+            mod.addAbstractTypeMapping(String.class, null);
+            fail("Should not pass");
+        } catch (IllegalArgumentException e) {
+            verifyException(e, "Cannot pass `null` as concrete type to map to");
+        }
+    }
+
+    public void testInvalidSubtypeMappings() throws Exception
+    {
+        SimpleModule mod = new SimpleModule("test", Version.unknownVersion(),
+                null, null);
+        try {
+            mod.registerSubtypes(String.class, null);
+            fail("Should not pass");
+        } catch (IllegalArgumentException e) {
+            verifyException(e, "Cannot pass `null` as subtype to register");
+        }
+
+        try {
+            mod.registerSubtypes(new NamedType(Integer.class), (NamedType) null);
+            fail("Should not pass");
+        } catch (IllegalArgumentException e) {
+            verifyException(e, "Cannot pass `null` as subtype to register");
+        }
+    }
+
+    public void testInvalidValueInstantiator() throws Exception
+    {
+        SimpleModule mod = new SimpleModule("test", Version.unknownVersion());
+
+        try {
+            mod.addValueInstantiator(null, null);
+            fail("Should not pass");
+        } catch (IllegalArgumentException e) {
+            verifyException(e, "Cannot pass `null` as class to register value instantiator for");
+        }
+        try {
+            mod.addValueInstantiator(CharSequence.class, null);
+            fail("Should not pass");
+        } catch (IllegalArgumentException e) {
+            verifyException(e, "Cannot pass `null` as value instantiator");
+        }
+    }
+
+    public void testInvalidMixIn() throws Exception
+    {
+        SimpleModule mod = new SimpleModule("test", Version.unknownVersion());
+
+        try {
+            mod.setMixInAnnotation(null, String.class);
+            fail("Should not pass");
+        } catch (IllegalArgumentException e) {
+            verifyException(e, "Cannot pass `null` as target type");
+        }
+        try {
+            mod.setMixInAnnotation(String.class, null);
+            fail("Should not pass");
+        } catch (IllegalArgumentException e) {
+            verifyException(e, "Cannot pass `null` as mixin class");
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/module/TestSimpleModule.java b/src/test/java/com/fasterxml/jackson/databind/module/SimpleModuleTest.java
similarity index 86%
rename from src/test/java/com/fasterxml/jackson/databind/module/TestSimpleModule.java
rename to src/test/java/com/fasterxml/jackson/databind/module/SimpleModuleTest.java
index 375b41c..40a189e 100644
--- a/src/test/java/com/fasterxml/jackson/databind/module/TestSimpleModule.java
+++ b/src/test/java/com/fasterxml/jackson/databind/module/SimpleModuleTest.java
@@ -11,18 +11,13 @@
 import com.fasterxml.jackson.databind.module.SimpleDeserializers;
 import com.fasterxml.jackson.databind.module.SimpleModule;
 import com.fasterxml.jackson.databind.module.SimpleSerializers;
+
 import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer;
 import com.fasterxml.jackson.databind.ser.std.StdSerializer;
 
 @SuppressWarnings("serial")
-public class TestSimpleModule extends BaseMapTest
+public class SimpleModuleTest extends BaseMapTest
 {
-    /*
-    /**********************************************************
-    /* Helper classes; simple beans and their handlers
-    /**********************************************************
-     */
-    
     /**
      * Trivial bean that requires custom serializer and deserializer
      */
@@ -151,7 +146,18 @@
         }
     }
 
-    protected static class ContextVerifierModule extends Module
+    /**
+     * Test module that is different from MySimpleModule. Used to test registration
+     * of multiple modules.
+     */
+    protected static class AnotherSimpleModule extends SimpleModule
+    {
+        public AnotherSimpleModule(String name, Version version) {
+            super(name, version);
+        }
+    }
+
+    protected static class ContextVerifierModule extends com.fasterxml.jackson.databind.Module
     {
         @Override
         public String getModuleName() { return "x"; }
@@ -210,7 +216,8 @@
             mapper.readValue("{\"str\":\"ab\",\"num\":2}", CustomBean.class);
             fail("Should have caused an exception");
         } catch (IOException e) {
-            verifyException(e, "No suitable constructor found");
+            verifyException(e, "Cannot construct");
+            verifyException(e, "no creators");
         }
     }
 
@@ -234,17 +241,19 @@
         ObjectMapper mapper = new ObjectMapper();
         SimpleModule mod = new SimpleModule("test", Version.unknownVersion());
         mod.addSerializer(new SimpleEnumSerializer());
-        mapper.registerModule(mod);
+        // for fun, call "multi-module" registration
+        mapper.registerModules(mod);
         assertEquals(quote("b"), mapper.writeValueAsString(SimpleEnum.B));
     }
 
-    // for [JACKSON-550]
     public void testSimpleInterfaceSerializer() throws Exception
     {
         ObjectMapper mapper = new ObjectMapper();
         SimpleModule mod = new SimpleModule("test", Version.unknownVersion());
         mod.addSerializer(new BaseSerializer());
-        mapper.registerModule(mod);
+        // and another variant here too
+        List<SimpleModule> mods = Arrays.asList(mod);
+        mapper.registerModules(mods);
         assertEquals(quote("Base:1"), mapper.writeValueAsString(new Impl1()));
         assertEquals(quote("Base:2"), mapper.writeValueAsString(new Impl2()));
     }
@@ -275,15 +284,17 @@
         SimpleEnum result = mapper.readValue(quote("a"), SimpleEnum.class);
         assertSame(SimpleEnum.A, result);
     }
- 
-    // Simple verification of [JACKSON-455]
+
     public void testMultipleModules() throws Exception
     {
         MySimpleModule mod1 = new MySimpleModule("test1", Version.unknownVersion());
         SimpleModule mod2 = new SimpleModule("test2", Version.unknownVersion());
         mod1.addSerializer(SimpleEnum.class, new SimpleEnumSerializer());
         mod1.addDeserializer(CustomBean.class, new CustomBeanDeserializer());
-        mod2.addDeserializer(SimpleEnum.class, new SimpleEnumDeserializer());
+
+        Map<Class<?>,JsonDeserializer<?>> desers = new HashMap<>();
+        desers.put(SimpleEnum.class, new SimpleEnumDeserializer());
+        mod2.setDeserializers(new SimpleDeserializers(desers));
         mod2.addSerializer(CustomBean.class, new CustomBeanSerializer());
 
         ObjectMapper mapper = new ObjectMapper();
@@ -302,13 +313,27 @@
         assertSame(SimpleEnum.A, result);
     }
 
+    public void testGetRegisteredModules()
+    {
+        MySimpleModule mod1 = new MySimpleModule("test1", Version.unknownVersion());
+        AnotherSimpleModule mod2 = new AnotherSimpleModule("test2", Version.unknownVersion());
+
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(mod1);
+        mapper.registerModule(mod2);
+
+        Set<Object> registeredModuleIds = mapper.getRegisteredModuleIds();
+        assertEquals(2, registeredModuleIds.size());
+        assertTrue(registeredModuleIds.contains(mod1.getTypeId()));
+        assertTrue(registeredModuleIds.contains(mod2.getTypeId()));
+    }
+
     /*
     /**********************************************************
     /* Unit tests; other
     /**********************************************************
      */
-    
-    // [JACKSON-644]: ability to register mix-ins
+
     public void testMixIns() throws Exception
     {
         SimpleModule module = new SimpleModule("test", Version.unknownVersion());
@@ -322,7 +347,6 @@
         assertEquals(Integer.valueOf(2), props.get("b"));
     }
 
-    // [JACKSON-686]
     public void testAccessToMapper() throws Exception
     {
         ContextVerifierModule module = new ContextVerifierModule();        
@@ -339,4 +363,10 @@
         Class<?> found = mapper.findMixInClassFor(Object.class);
         assertEquals(String.class, found);
     }
+
+    public void testAutoDiscovery() throws Exception
+    {
+        List<?> mods = ObjectMapper.findModules();
+        assertEquals(0, mods.size());
+    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/module/TestAbstractTypes.java b/src/test/java/com/fasterxml/jackson/databind/module/TestAbstractTypes.java
index 76402b0..f007b91 100644
--- a/src/test/java/com/fasterxml/jackson/databind/module/TestAbstractTypes.java
+++ b/src/test/java/com/fasterxml/jackson/databind/module/TestAbstractTypes.java
@@ -2,6 +2,8 @@
 
 import java.util.*;
 
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.core.Version;
 import com.fasterxml.jackson.databind.BaseMapTest;
 import com.fasterxml.jackson.databind.ObjectMapper;
@@ -38,10 +40,48 @@
         public int getValue() { return 3; }
     }
 
+    // [databind#2019]: mappings from multiple modules
+    public interface Datatype1 {
+        String getValue();
+    }
+
+    public interface Datatype2 {
+        String getValue();
+    }
+
+    static class SimpleDatatype1 implements Datatype1 {
+
+        private final String value;
+
+        @JsonCreator
+        public SimpleDatatype1(@JsonProperty("value") String value) {
+            this.value = value;
+        }
+
+        @Override
+        public String getValue() {
+            return value;
+        }
+    }
+
+    static class SimpleDatatype2 implements Datatype2 {
+        private final String value;
+
+        @JsonCreator
+        public SimpleDatatype2(@JsonProperty("value") String value) {
+            this.value = value;
+        }
+
+        @Override
+        public String getValue() {
+            return value;
+        }
+    }
+    
     /*
-    /**********************************************************
+    /**********************************************************************
     /* Test methods
-    /**********************************************************
+    /**********************************************************************
      */
 
     public void testCollectionDefaulting() throws Exception
@@ -115,4 +155,23 @@
         Abstract a = mapper.readValue("{}", Abstract.class);
         assertNotNull(a);
     }
+
+    // [databind#2019]: mappings from multiple modules
+    public static void testAbstractMappingsFromTwoModules() throws Exception
+    {
+        ObjectMapper mapper = newObjectMapper();
+        SimpleModule module1 = new SimpleModule("module1");
+        module1.addAbstractTypeMapping(Datatype1.class, SimpleDatatype1.class);
+
+        SimpleModule module2 = new SimpleModule("module2");
+        module2.addAbstractTypeMapping(Datatype2.class, SimpleDatatype2.class);
+        mapper.registerModules(module1, module2);
+
+        final String JSON_EXAMPLE = "{\"value\": \"aaa\"}";
+        Datatype1 value1 = mapper.readValue(JSON_EXAMPLE, Datatype1.class);
+        assertNotNull(value1);
+
+        Datatype2 value2 = mapper.readValue(JSON_EXAMPLE, Datatype2.class);
+        assertNotNull(value2);
+    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/module/TestCustomEnumKeyDeserializer.java b/src/test/java/com/fasterxml/jackson/databind/module/TestCustomEnumKeyDeserializer.java
index f99e320..bc5a53c 100644
--- a/src/test/java/com/fasterxml/jackson/databind/module/TestCustomEnumKeyDeserializer.java
+++ b/src/test/java/com/fasterxml/jackson/databind/module/TestCustomEnumKeyDeserializer.java
@@ -4,8 +4,6 @@
 import java.io.IOException;
 import java.util.*;
 
-import org.junit.Test;
-
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
 import com.fasterxml.jackson.core.*;
 import com.fasterxml.jackson.core.type.TypeReference;
@@ -169,7 +167,6 @@
      */
     
     // Test passing with the fix
-    @Test
     public void testWithEnumKeys() throws Exception {
         ObjectMapper plainObjectMapper = new ObjectMapper();
         JsonNode tree = plainObjectMapper.readTree(aposToQuotes("{'red' : [ 'a', 'b']}"));
diff --git a/src/test/java/com/fasterxml/jackson/databind/module/TestDuplicateRegistration.java b/src/test/java/com/fasterxml/jackson/databind/module/TestDuplicateRegistration.java
index 171b867..9adca9c 100644
--- a/src/test/java/com/fasterxml/jackson/databind/module/TestDuplicateRegistration.java
+++ b/src/test/java/com/fasterxml/jackson/databind/module/TestDuplicateRegistration.java
@@ -5,7 +5,7 @@
 
 public class TestDuplicateRegistration extends BaseMapTest
 {
-    static class MyModule extends Module {
+    static class MyModule extends com.fasterxml.jackson.databind.Module {
         public int regCount;
         
         public MyModule() {
diff --git a/src/test/java/com/fasterxml/jackson/databind/module/TestKeyDeserializers.java b/src/test/java/com/fasterxml/jackson/databind/module/TestKeyDeserializers.java
index a3f2227..a9bd0d8 100644
--- a/src/test/java/com/fasterxml/jackson/databind/module/TestKeyDeserializers.java
+++ b/src/test/java/com/fasterxml/jackson/databind/module/TestKeyDeserializers.java
@@ -4,10 +4,7 @@
 
 import com.fasterxml.jackson.core.Version;
 import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.BaseMapTest;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.KeyDeserializer;
-import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.*;
 
 public class TestKeyDeserializers extends BaseMapTest
 {
@@ -25,7 +22,6 @@
         
         public Foo(String v) { value = v; }
     }
-    
 
     /*
     /**********************************************************
diff --git a/src/test/java/com/fasterxml/jackson/databind/node/ArrayNodeTest.java b/src/test/java/com/fasterxml/jackson/databind/node/ArrayNodeTest.java
new file mode 100644
index 0000000..9a1a59f
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/node/ArrayNodeTest.java
@@ -0,0 +1,341 @@
+package com.fasterxml.jackson.databind.node;
+
+import java.io.*;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.ArrayList;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.exc.MismatchedInputException;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.TextNode;
+import com.fasterxml.jackson.databind.node.TreeTraversingParser;
+
+/**
+ * Additional tests for {@link ArrayNode} container class.
+ */
+public class ArrayNodeTest
+    extends BaseMapTest
+{
+    public void testDirectCreation() throws IOException
+    {
+        ArrayNode n = new ArrayNode(JsonNodeFactory.instance);
+        assertStandardEquals(n);
+        assertFalse(n.elements().hasNext());
+        assertFalse(n.fieldNames().hasNext());
+        TextNode text = TextNode.valueOf("x");
+        n.add(text);
+        assertEquals(1, n.size());
+        assertFalse(0 == n.hashCode());
+        assertTrue(n.elements().hasNext());
+        // no field names for arrays
+        assertFalse(n.fieldNames().hasNext());
+        assertNull(n.get("x")); // not used with arrays
+        assertTrue(n.path("x").isMissingNode());
+        assertSame(text, n.get(0));
+
+        // single element, so:
+        assertFalse(n.has("field"));
+        assertFalse(n.hasNonNull("field"));
+        assertTrue(n.has(0));
+        assertTrue(n.hasNonNull(0));
+        assertFalse(n.has(1));
+        assertFalse(n.hasNonNull(1));
+        
+        // add null node too
+        n.add((JsonNode) null);
+        assertEquals(2, n.size());
+        assertTrue(n.get(1).isNull());
+        assertTrue(n.has(1));
+        assertFalse(n.hasNonNull(1));
+        // change to text
+        n.set(1, text);
+        assertSame(text, n.get(1));
+        n.set(0, null);
+        assertTrue(n.get(0).isNull());
+
+        // and finally, clear it all
+        ArrayNode n2 = new ArrayNode(JsonNodeFactory.instance);
+        n2.add("foobar");
+        assertFalse(n.equals(n2));
+        n.addAll(n2);
+        assertEquals(3, n.size());
+
+        assertFalse(n.get(0).isTextual());
+        assertNotNull(n.remove(0));
+        assertEquals(2, n.size());
+        assertTrue(n.get(0).isTextual());
+        assertNull(n.remove(-1));
+        assertNull(n.remove(100));
+        assertEquals(2, n.size());
+
+        ArrayList<JsonNode> nodes = new ArrayList<JsonNode>();
+        nodes.add(text);
+        n.addAll(nodes);
+        assertEquals(3, n.size());
+        assertNull(n.get(10000));
+        assertNull(n.remove(-4));
+
+        TextNode text2 = TextNode.valueOf("b");
+        n.insert(0, text2);
+        assertEquals(4, n.size());
+        assertSame(text2, n.get(0));
+
+        assertNotNull(n.addArray());
+        assertEquals(5, n.size());
+        n.addPOJO("foo");
+        assertEquals(6, n.size());
+
+        n.removeAll();
+        assertEquals(0, n.size());
+    }
+
+    public void testDirectCreation2() throws IOException
+    {
+        JsonNodeFactory f = objectMapper().getNodeFactory();
+        ArrayList<JsonNode> list = new ArrayList<>();
+        list.add(f.booleanNode(true));
+        list.add(f.textNode("foo"));
+        ArrayNode n = new ArrayNode(f, list);
+        assertEquals(2, n.size());
+        assertTrue(n.get(0).isBoolean());
+        assertTrue(n.get(1).isTextual());
+
+        // also, should fail with invalid set attempt
+        try {
+            n.set(2, f.nullNode());
+            fail("Should not pass");
+        } catch (IndexOutOfBoundsException e) {
+            verifyException(e, "illegal index");
+        }
+        n.insert(1, (String) null);
+        assertEquals(3, n.size());
+        assertTrue(n.get(0).isBoolean());
+        assertTrue(n.get(1).isNull());
+        assertTrue(n.get(2).isTextual());
+
+        n.removeAll();
+        n.insert(0, (JsonNode) null);
+        assertEquals(1, n.size());
+        assertTrue(n.get(0).isNull());
+    }
+
+    public void testArrayViaMapper() throws Exception
+    {
+        final String JSON = "[[[-0.027512,51.503221],[-0.008497,51.503221],[-0.008497,51.509744],[-0.027512,51.509744]]]";
+
+        JsonNode n = objectMapper().readTree(JSON);
+        assertNotNull(n);
+        assertTrue(n.isArray());
+        ArrayNode an = (ArrayNode) n;
+        assertEquals(1, an.size());
+        ArrayNode an2 = (ArrayNode) n.get(0);
+        assertTrue(an2.isArray());
+        assertEquals(4, an2.size());
+    }
+
+    public void testAdds()
+    {
+        ArrayNode n = new ArrayNode(JsonNodeFactory.instance);
+        assertNotNull(n.addArray());
+        assertNotNull(n.addObject());
+        n.addPOJO("foobar");
+        n.add(1);
+        n.add(1L);
+        n.add(0.5);
+        n.add(0.5f);
+        n.add(new BigDecimal("0.2"));
+        n.add(BigInteger.TEN);
+        assertEquals(9, n.size());
+
+        assertNotNull(n.insertArray(0));
+        assertNotNull(n.insertObject(0));
+        n.insertPOJO(2, "xxx");
+        assertEquals(12, n.size());
+
+        n.insert(0, BigInteger.ONE);
+        n.insert(0, new BigDecimal("0.1"));
+        assertEquals(14, n.size());
+    }
+
+    public void testNullAdds()
+    {
+        JsonNodeFactory f = objectMapper().getNodeFactory();
+        ArrayNode array = f.arrayNode(14);
+
+        array.add((BigDecimal) null);
+        array.add((BigInteger) null);
+        array.add((Boolean) null);
+        array.add((byte[]) null);
+        array.add((Double) null);
+        array.add((Float) null);
+        array.add((Integer) null);
+        array.add((JsonNode) null);
+        array.add((Long) null);
+        array.add((String) null);
+
+        assertEquals(10, array.size());
+        
+        for (JsonNode node : array) {
+            assertTrue(node.isNull());
+        }
+    }
+
+    public void testNullInserts()
+    {
+        JsonNodeFactory f = objectMapper().getNodeFactory();
+        ArrayNode array = f.arrayNode(3);
+
+        array.insert(0, (BigDecimal) null);
+        array.insert(0, (BigInteger) null);
+        array.insert(0, (Boolean) null);
+        // Offsets out of the range are fine; negative become 0;
+        // super big just add at the end
+        array.insert(-56, (byte[]) null);
+        array.insert(0, (Double) null);
+        array.insert(200, (Float) null);
+        array.insert(0, (Integer) null);
+        array.insert(1, (JsonNode) null);
+        array.insert(array.size(), (Long) null);
+        array.insert(1, (String) null);
+
+        assertEquals(10, array.size());
+        
+        for (JsonNode node : array) {
+            assertTrue(node.isNull());
+        }
+    }
+    
+    public void testNullChecking()
+    {
+        ArrayNode a1 = JsonNodeFactory.instance.arrayNode();
+        ArrayNode a2 = JsonNodeFactory.instance.arrayNode();
+        // used to throw NPE before fix:
+        a1.addAll(a2);
+        assertEquals(0, a1.size());
+        assertEquals(0, a2.size());
+
+        a2.addAll(a1);
+        assertEquals(0, a1.size());
+        assertEquals(0, a2.size());
+    }
+
+    public void testNullChecking2()
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        ArrayNode src = mapper.createArrayNode();
+        ArrayNode dest = mapper.createArrayNode();
+        src.add("element");
+        dest.addAll(src);
+    }
+    
+    public void testParser() throws Exception
+    {
+        ArrayNode n = new ArrayNode(JsonNodeFactory.instance);
+        n.add(123);
+        TreeTraversingParser p = new TreeTraversingParser(n, null);
+        p.setCodec(null);
+        assertNull(p.getCodec());
+        assertNotNull(p.getParsingContext());
+        assertNotNull(p.getTokenLocation());
+        assertNotNull(p.getCurrentLocation());
+        assertNull(p.getEmbeddedObject());
+        assertNull(p.currentNode());
+
+        //assertNull(p.getNumberType());
+
+        assertToken(JsonToken.START_ARRAY, p.nextToken());
+        p.skipChildren();
+        assertToken(JsonToken.END_ARRAY, p.getCurrentToken());
+        p.close();
+
+        p = new TreeTraversingParser(n, null);
+        p.nextToken();
+        assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+        assertEquals(JsonParser.NumberType.INT, p.getNumberType());
+        p.close();
+    }
+
+    public void testArrayNodeEquality()
+    {
+        ArrayNode n1 = new ArrayNode(null);
+        ArrayNode n2 = new ArrayNode(null);
+
+        assertTrue(n1.equals(n2));
+        assertTrue(n2.equals(n1));
+
+        n1.add(TextNode.valueOf("Test"));
+
+        assertFalse(n1.equals(n2));
+        assertFalse(n2.equals(n1));
+
+        n2.add(TextNode.valueOf("Test"));
+
+        assertTrue(n1.equals(n2));
+        assertTrue(n2.equals(n1));
+    }
+
+    public void testSimpleArray() throws Exception
+    {
+        ArrayNode result = objectMapper().createArrayNode();
+
+        assertTrue(result.isArray());
+        assertType(result, ArrayNode.class);
+
+        assertFalse(result.isObject());
+        assertFalse(result.isNumber());
+        assertFalse(result.isNull());
+        assertFalse(result.isTextual());
+
+        // and let's add stuff...
+        result.add(false);
+        result.insertNull(0);
+
+        // should be equal to itself no matter what
+        assertEquals(result, result);
+        assertFalse(result.equals(null)); // but not to null
+
+        // plus see that we can access stuff
+        assertEquals(NullNode.instance, result.path(0));
+        assertEquals(NullNode.instance, result.get(0));
+        assertEquals(BooleanNode.FALSE, result.path(1));
+        assertEquals(BooleanNode.FALSE, result.get(1));
+        assertEquals(2, result.size());
+
+        assertNull(result.get(-1));
+        assertNull(result.get(2));
+        JsonNode missing = result.path(2);
+        assertTrue(missing.isMissingNode());
+        assertTrue(result.path(-100).isMissingNode());
+
+        // then construct and compare
+        ArrayNode array2 = objectMapper().createArrayNode();
+        array2.addNull();
+        array2.add(false);
+        assertEquals(result, array2);
+
+        // plus remove entries
+        JsonNode rm1 = array2.remove(0);
+        assertEquals(NullNode.instance, rm1);
+        assertEquals(1, array2.size());
+        assertEquals(BooleanNode.FALSE, array2.get(0));
+        assertFalse(result.equals(array2));
+
+        JsonNode rm2 = array2.remove(0);
+        assertEquals(BooleanNode.FALSE, rm2);
+        assertEquals(0, array2.size());
+    }
+
+    public void testSimpleMismatch() throws Exception
+    {
+        ObjectMapper mapper = objectMapper();
+        try {
+            mapper.readValue(" 123 ", ArrayNode.class);
+            fail("Should not pass");
+        } catch (MismatchedInputException e) {
+            verifyException(e, "out of VALUE_NUMBER_INT token");
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/node/JsonNodeFactoryTest.java b/src/test/java/com/fasterxml/jackson/databind/node/JsonNodeFactoryTest.java
new file mode 100644
index 0000000..3a646b4
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/node/JsonNodeFactoryTest.java
@@ -0,0 +1,37 @@
+package com.fasterxml.jackson.databind.node;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import com.fasterxml.jackson.databind.*;
+
+public class JsonNodeFactoryTest extends NodeTestBase
+{
+    private final ObjectMapper MAPPER = objectMapper();
+
+    public void testSimpleCreation()
+    {
+        JsonNodeFactory f = MAPPER.getNodeFactory();
+        JsonNode n;
+
+        n = f.numberNode((byte) 4);
+        assertTrue(n.isInt());
+        assertEquals(4, n.intValue());
+
+        assertTrue(f.numberNode((Byte) null).isNull());
+
+        assertTrue(f.numberNode((Short) null).isNull());
+
+        assertTrue(f.numberNode((Integer) null).isNull());
+
+        assertTrue(f.numberNode((Long) null).isNull());
+
+        assertTrue(f.numberNode((Float) null).isNull());
+
+        assertTrue(f.numberNode((Double) null).isNull());
+
+        assertTrue(f.numberNode((BigDecimal) null).isNull());
+
+        assertTrue(f.numberNode((BigInteger) null).isNull());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/node/TestNumberNodes.java b/src/test/java/com/fasterxml/jackson/databind/node/NumberNodesTest.java
similarity index 75%
rename from src/test/java/com/fasterxml/jackson/databind/node/TestNumberNodes.java
rename to src/test/java/com/fasterxml/jackson/databind/node/NumberNodesTest.java
index 62610f0..f12cb94 100644
--- a/src/test/java/com/fasterxml/jackson/databind/node/TestNumberNodes.java
+++ b/src/test/java/com/fasterxml/jackson/databind/node/NumberNodesTest.java
@@ -12,8 +12,10 @@
  * Basic tests for {@link JsonNode} implementations that
  * contain numeric values.
  */
-public class TestNumberNodes extends NodeTestBase
+public class NumberNodesTest extends NodeTestBase
 {
+    private final ObjectMapper MAPPER = objectMapper();
+    
     public void testShort()
     {
         ShortNode n = ShortNode.valueOf((short) 1);
@@ -37,8 +39,33 @@
         assertTrue(ShortNode.valueOf(Short.MAX_VALUE).canConvertToLong());
         assertTrue(ShortNode.valueOf(Short.MIN_VALUE).canConvertToLong());
     }
-    
-	public void testInt()
+
+    public void testIntViaMapper() throws Exception
+    {
+        int value = -90184;
+        JsonNode result = MAPPER.readTree(String.valueOf(value));
+        assertTrue(result.isNumber());
+        assertTrue(result.isIntegralNumber());
+        assertTrue(result.isInt());
+        assertType(result, IntNode.class);
+        assertFalse(result.isLong());
+        assertFalse(result.isFloatingPointNumber());
+        assertFalse(result.isDouble());
+        assertFalse(result.isNull());
+        assertFalse(result.isTextual());
+        assertFalse(result.isMissingNode());
+
+        assertEquals(value, result.numberValue().intValue());
+        assertEquals(value, result.intValue());
+        assertEquals(String.valueOf(value), result.asText());
+        assertEquals((double) value, result.doubleValue());
+        assertEquals((long) value, result.longValue());
+
+        // also, equality should work ok
+        assertEquals(result, IntNode.valueOf(value));
+    }
+
+    public void testInt()
     {
         IntNode n = IntNode.valueOf(1);
         assertStandardEquals(n);
@@ -62,6 +89,7 @@
         assertTrue(IntNode.valueOf(0).canConvertToLong());
         assertTrue(IntNode.valueOf(Integer.MAX_VALUE).canConvertToLong());
         assertTrue(IntNode.valueOf(Integer.MIN_VALUE).canConvertToLong());
+
     }
 
     public void testLong()
@@ -92,6 +120,31 @@
         assertTrue(LongNode.valueOf(Long.MIN_VALUE).canConvertToLong());
     }
 
+    public void testLongViaMapper() throws Exception
+    {
+        // need to use something being 32-bit value space
+        long value = 12345678L << 32;
+        JsonNode result = MAPPER.readTree(String.valueOf(value));
+        assertTrue(result.isNumber());
+        assertTrue(result.isIntegralNumber());
+        assertTrue(result.isLong());
+        assertType(result, LongNode.class);
+        assertFalse(result.isInt());
+        assertFalse(result.isFloatingPointNumber());
+        assertFalse(result.isDouble());
+        assertFalse(result.isNull());
+        assertFalse(result.isTextual());
+        assertFalse(result.isMissingNode());
+
+        assertEquals(value, result.numberValue().longValue());
+        assertEquals(value, result.longValue());
+        assertEquals(String.valueOf(value), result.asText());
+        assertEquals((double) value, result.doubleValue());
+
+        // also, equality should work ok
+        assertEquals(result, LongNode.valueOf(value));
+    }
+
     public void testDouble() throws Exception
     {
         DoubleNode n = DoubleNode.valueOf(0.25);
@@ -105,7 +158,6 @@
         assertEquals(BigInteger.ZERO, n.bigIntegerValue());
         assertEquals("0.25", n.asText());
 
-        // 1.6:
         assertNodeNumbers(DoubleNode.valueOf(4.5), 4, 4.5);
 
         assertTrue(DoubleNode.valueOf(0).canConvertToInt());
@@ -125,6 +177,31 @@
         assertEquals("-0.0", String.valueOf(n.doubleValue()));
     }
 
+    public void testDoubleViaMapper() throws Exception
+    {
+        double value = 3.04;
+        JsonNode result = MAPPER.readTree(String.valueOf(value));
+        assertTrue(result.isNumber());
+        assertFalse(result.isNull());
+        assertType(result, DoubleNode.class);
+        assertTrue(result.isFloatingPointNumber());
+        assertTrue(result.isDouble());
+        assertFalse(result.isInt());
+        assertFalse(result.isLong());
+        assertFalse(result.isIntegralNumber());
+        assertFalse(result.isTextual());
+        assertFalse(result.isMissingNode());
+
+        assertEquals(value, result.doubleValue());
+        assertEquals(value, result.numberValue().doubleValue());
+        assertEquals((int) value, result.intValue());
+        assertEquals((long) value, result.longValue());
+        assertEquals(String.valueOf(value), result.asText());
+
+        // also, equality should work ok
+        assertEquals(result, DoubleNode.valueOf(value));
+    }
+
     // @since 2.2
     public void testFloat()
     {
@@ -173,6 +250,7 @@
         assertEquals(JsonParser.NumberType.BIG_DECIMAL, n.numberType());
         assertTrue(n.isNumber());
         assertFalse(n.isIntegralNumber());
+        assertFalse(n.isArray());
         assertTrue(n.isBigDecimal());
         assertEquals(BigDecimal.ONE, n.numberValue());
         assertEquals(1, n.intValue());
@@ -180,7 +258,6 @@
         assertEquals(BigDecimal.ONE, n.decimalValue());
         assertEquals("1", n.asText());
 
-        // 1.6:
         assertNodeNumbers(n, 1, 1.0);
 
         assertTrue(DecimalNode.valueOf(BigDecimal.ZERO).canConvertToInt());
@@ -192,8 +269,31 @@
         assertTrue(DecimalNode.valueOf(BigDecimal.ZERO).canConvertToLong());
         assertTrue(DecimalNode.valueOf(BigDecimal.valueOf(Long.MAX_VALUE)).canConvertToLong());
         assertTrue(DecimalNode.valueOf(BigDecimal.valueOf(Long.MIN_VALUE)).canConvertToLong());
-    }
 
+        // no "natural" way to get it, must construct
+        BigDecimal value = new BigDecimal("0.1");
+        JsonNode result = DecimalNode.valueOf(value);
+
+        assertFalse(result.isObject());
+        assertTrue(result.isNumber());
+        assertFalse(result.isIntegralNumber());
+        assertFalse(result.isLong());
+        assertType(result, DecimalNode.class);
+        assertFalse(result.isInt());
+        assertTrue(result.isFloatingPointNumber());
+        assertTrue(result.isBigDecimal());
+        assertFalse(result.isDouble());
+        assertFalse(result.isNull());
+        assertFalse(result.isTextual());
+        assertFalse(result.isMissingNode());
+
+        assertEquals(value, result.numberValue());
+        assertEquals(value.toString(), result.asText());
+
+        // also, equality should work ok
+        assertEquals(result, DecimalNode.valueOf(value));
+    }
+    
     public void testDecimalNodeEqualsHashCode()
     {
         // We want DecimalNodes with equivalent _numeric_ values to be equal;
@@ -290,9 +390,8 @@
         assertFalse(n.isInt());
         assertTrue(n.isLong());
 
-        /* 19-May-2015, tatu: Actually, no, coercion should not happen by default.
-         *   But it should be possible to change it if necessary.
-         */
+        // 19-May-2015, tatu: Actually, no, coercion should not happen by default.
+        //   But it should be possible to change it if necessary.
         // but "too small" number will be 'int'...
         n = f.numberNode(123L);
         assertTrue(n.isLong());
diff --git a/src/test/java/com/fasterxml/jackson/databind/node/TestObjectNode.java b/src/test/java/com/fasterxml/jackson/databind/node/ObjectNodeTest.java
similarity index 90%
rename from src/test/java/com/fasterxml/jackson/databind/node/TestObjectNode.java
rename to src/test/java/com/fasterxml/jackson/databind/node/ObjectNodeTest.java
index 719c109..b617d68 100644
--- a/src/test/java/com/fasterxml/jackson/databind/node/TestObjectNode.java
+++ b/src/test/java/com/fasterxml/jackson/databind/node/ObjectNodeTest.java
@@ -1,6 +1,7 @@
 package com.fasterxml.jackson.databind.node;
 
 import java.math.BigDecimal;
+import java.math.BigInteger;
 import java.util.*;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
@@ -8,11 +9,12 @@
 import com.fasterxml.jackson.annotation.JsonValue;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.exc.MismatchedInputException;
 
 /**
  * Additional tests for {@link ObjectNode} container class.
  */
-public class TestObjectNode
+public class ObjectNodeTest
     extends BaseMapTest
 {
     @JsonDeserialize(as = DataImpl.class)
@@ -80,7 +82,6 @@
         assertTrue(root.isObject());
         assertEquals(2, root.size());
 
-        // Related to [JACKSON-50]:
         Iterator<JsonNode> it = root.iterator();
         assertNotNull(it);
         assertTrue(it.hasNext());
@@ -115,7 +116,7 @@
         assertEquals(IntNode.valueOf(1), root.get("key"));
         assertNull(root.get("b"));
     }    
-    // for [Issue#346]
+    // for [databind#346]
     public void testEmptyNodeAsValue() throws Exception
     {
         Data w = MAPPER.readValue("{}", Data.class);
@@ -178,6 +179,24 @@
         assertEquals(0, n.size());
     }
 
+    public void testBigNumbers()
+    {
+        ObjectNode n = new ObjectNode(JsonNodeFactory.instance);
+        assertStandardEquals(n);
+        BigInteger I = BigInteger.valueOf(3);
+        BigDecimal DEC = new BigDecimal("0.1");
+
+        n.put("a", DEC);
+        n.put("b", I);
+
+        assertEquals(2, n.size());
+
+        assertTrue(n.path("a").isBigDecimal());
+        assertEquals(DEC, n.get("a").decimalValue());
+        assertTrue(n.path("b").isBigInteger());
+        assertEquals(I, n.get("b").bigIntegerValue());
+    }
+
     /**
      * Verify null handling
      */
@@ -205,6 +224,13 @@
         n = o1.get("d");
         assertNotNull(n);
         assertSame(n, NullNode.instance);
+
+        o1.put("3", (BigInteger) null);
+        n = o1.get("3");
+        assertNotNull(3);
+        assertSame(n, NullNode.instance);
+
+        assertEquals(4, o1.size());
     }
 
     /**
@@ -340,7 +366,7 @@
         assertEquals(1, root3.path("a").intValue());
     }
 
-    // [Issue#237] (databind): support DeserializationFeature#FAIL_ON_READING_DUP_TREE_KEY
+    // [databind#237] (databind): support DeserializationFeature#FAIL_ON_READING_DUP_TREE_KEY
     public void testFailOnDupKeys() throws Exception
     {
         final String DUP_JSON = "{ \"a\":1, \"a\":2 }";
@@ -413,4 +439,15 @@
 //        System.out.println("Deserialized to MyValue: "+de2);
         assertNotNull(de2);
     }
+
+    public void testSimpleMismatch() throws Exception
+    {
+        ObjectMapper mapper = objectMapper();
+        try {
+            mapper.readValue("[ 1, 2, 3 ]", ObjectNode.class);
+            fail("Should not pass");
+        } catch (MismatchedInputException e) {
+            verifyException(e, "out of START_ARRAY token");
+        }
+    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/node/POJONodeTest.java b/src/test/java/com/fasterxml/jackson/databind/node/POJONodeTest.java
new file mode 100644
index 0000000..638cb43
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/node/POJONodeTest.java
@@ -0,0 +1,55 @@
+package com.fasterxml.jackson.databind.node;
+
+import java.io.IOException;
+import java.util.*;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+
+public class POJONodeTest extends NodeTestBase
+{
+    @JsonSerialize(using = CustomSer.class)
+    public static class Data {
+      public String aStr;
+    }
+
+    @SuppressWarnings("serial")
+    public static class CustomSer extends StdSerializer<Data> {
+      public CustomSer() {
+          super(Data.class);
+      }
+
+      @Override
+      public void serialize(Data value, JsonGenerator gen, SerializerProvider provider) throws IOException {
+        String attrStr = (String) provider.getAttribute("myAttr");
+        gen.writeStartObject();
+        gen.writeStringField("aStr", "The value is: " + (attrStr == null ? "NULL" : attrStr));
+        gen.writeEndObject();
+      }
+    }
+
+    final ObjectMapper MAPPER = newObjectMapper();
+
+    public void testPOJONodeCustomSer() throws Exception
+    {
+      Data data = new Data();
+      data.aStr = "Hello";
+
+      Map<String, Object> mapTest = new HashMap<>();
+      mapTest.put("data", data);
+
+      ObjectNode treeTest = MAPPER.createObjectNode();
+      treeTest.putPOJO("data", data);
+
+      final String EXP = "{\"data\":{\"aStr\":\"The value is: Hello!\"}}";
+      
+      String mapOut = MAPPER.writer().withAttribute("myAttr", "Hello!").writeValueAsString(mapTest);
+      assertEquals(EXP, mapOut);
+
+      String treeOut = MAPPER.writer().withAttribute("myAttr", "Hello!").writeValueAsString(treeTest);
+      assertEquals(EXP, treeOut);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/node/TestArrayNode.java b/src/test/java/com/fasterxml/jackson/databind/node/TestArrayNode.java
deleted file mode 100644
index 901910e..0000000
--- a/src/test/java/com/fasterxml/jackson/databind/node/TestArrayNode.java
+++ /dev/null
@@ -1,168 +0,0 @@
-package com.fasterxml.jackson.databind.node;
-
-import java.io.*;
-import java.util.*;
-
-
-import com.fasterxml.jackson.core.*;
-import com.fasterxml.jackson.databind.*;
-import com.fasterxml.jackson.databind.node.ArrayNode;
-import com.fasterxml.jackson.databind.node.JsonNodeFactory;
-import com.fasterxml.jackson.databind.node.TextNode;
-import com.fasterxml.jackson.databind.node.TreeTraversingParser;
-
-/**
- * Additional tests for {@link ArrayNode} container class.
- */
-public class TestArrayNode
-    extends BaseMapTest
-{
-    public void testBasics() throws IOException
-    {
-        ArrayNode n = new ArrayNode(JsonNodeFactory.instance);
-        assertStandardEquals(n);
-        assertFalse(n.elements().hasNext());
-        assertFalse(n.fieldNames().hasNext());
-        TextNode text = TextNode.valueOf("x");
-        n.add(text);
-        assertEquals(1, n.size());
-        assertFalse(0 == n.hashCode());
-        assertTrue(n.elements().hasNext());
-        // no field names for arrays
-        assertFalse(n.fieldNames().hasNext());
-        assertNull(n.get("x")); // not used with arrays
-        assertTrue(n.path("x").isMissingNode());
-        assertSame(text, n.get(0));
-
-        // single element, so:
-        assertFalse(n.has("field"));
-        assertFalse(n.hasNonNull("field"));
-        assertTrue(n.has(0));
-        assertTrue(n.hasNonNull(0));
-        assertFalse(n.has(1));
-        assertFalse(n.hasNonNull(1));
-        
-        // add null node too
-        n.add((JsonNode) null);
-        assertEquals(2, n.size());
-        assertTrue(n.get(1).isNull());
-        assertTrue(n.has(1));
-        assertFalse(n.hasNonNull(1));
-        // change to text
-        n.set(1, text);
-        assertSame(text, n.get(1));
-        n.set(0, null);
-        assertTrue(n.get(0).isNull());
-
-        // and finally, clear it all
-        ArrayNode n2 = new ArrayNode(JsonNodeFactory.instance);
-        n2.add("foobar");
-        assertFalse(n.equals(n2));
-        n.addAll(n2);
-        assertEquals(3, n.size());
-
-        assertFalse(n.get(0).isTextual());
-        assertNotNull(n.remove(0));
-        assertEquals(2, n.size());
-        assertTrue(n.get(0).isTextual());
-
-        ArrayList<JsonNode> nodes = new ArrayList<JsonNode>();
-        nodes.add(text);
-        n.addAll(nodes);
-        assertEquals(3, n.size());
-        assertNull(n.get(10000));
-        assertNull(n.remove(-4));
-
-        TextNode text2 = TextNode.valueOf("b");
-        n.insert(0, text2);
-        assertEquals(4, n.size());
-        assertSame(text2, n.get(0));
-
-        assertNotNull(n.addArray());
-        assertEquals(5, n.size());
-        n.addPOJO("foo");
-        assertEquals(6, n.size());
-
-        // Try serializing it for fun, too...
-        JsonGenerator jg = new MappingJsonFactory().createGenerator(new StringWriter());
-        n.serialize(jg, null);
-
-        n.removeAll();
-        assertEquals(0, n.size());
-        jg.close();
-    }
-
-    public void testAdds()
-    {
-        ArrayNode n = new ArrayNode(JsonNodeFactory.instance);
-        assertNotNull(n.addArray());
-        assertNotNull(n.addObject());
-        n.addPOJO("foobar");
-        n.add(1);
-        n.add(1L);
-        n.add(0.5);
-        n.add(0.5f);
-        assertEquals(7, n.size());
-
-        assertNotNull(n.insertArray(0));
-        assertNotNull(n.insertObject(0));
-        n.insertPOJO(2, "xxx");
-        assertEquals(10, n.size());
-    }
-
-    /**
-     * Test to verify [JACKSON-227]
-     */
-    public void testNullChecking()
-    {
-        ArrayNode a1 = JsonNodeFactory.instance.arrayNode();
-        ArrayNode a2 = JsonNodeFactory.instance.arrayNode();
-        // used to throw NPE before fix:
-        a1.addAll(a2);
-        assertEquals(0, a1.size());
-        assertEquals(0, a2.size());
-
-        a2.addAll(a1);
-        assertEquals(0, a1.size());
-        assertEquals(0, a2.size());
-    }
-
-    /**
-     * Another test to verify [JACKSON-227]...
-     */
-    public void testNullChecking2()
-    {
-        ObjectMapper mapper = new ObjectMapper();
-        ArrayNode src = mapper.createArrayNode();
-        ArrayNode dest = mapper.createArrayNode();
-        src.add("element");
-        dest.addAll(src);
-    }
-    
-    public void testParser() throws Exception
-    {
-        ArrayNode n = new ArrayNode(JsonNodeFactory.instance);
-        n.add(123);
-        TreeTraversingParser p = new TreeTraversingParser(n, null);
-        p.setCodec(null);
-        assertNull(p.getCodec());
-        assertNotNull(p.getParsingContext());
-        assertNotNull(p.getTokenLocation());
-        assertNotNull(p.getCurrentLocation());
-        assertNull(p.getEmbeddedObject());
-        assertNull(p.currentNode());
-
-        //assertNull(p.getNumberType());
-
-        assertToken(JsonToken.START_ARRAY, p.nextToken());
-        p.skipChildren();
-        assertToken(JsonToken.END_ARRAY, p.getCurrentToken());
-        p.close();
-
-        p = new TreeTraversingParser(n, null);
-        p.nextToken();
-        assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
-        assertEquals(JsonParser.NumberType.INT, p.getNumberType());
-        p.close();
-    }
-}
diff --git a/src/test/java/com/fasterxml/jackson/databind/node/TestConversions.java b/src/test/java/com/fasterxml/jackson/databind/node/TestConversions.java
index 354793a..fedf874 100644
--- a/src/test/java/com/fasterxml/jackson/databind/node/TestConversions.java
+++ b/src/test/java/com/fasterxml/jackson/databind/node/TestConversions.java
@@ -10,6 +10,7 @@
 
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
 import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.type.WritableTypeId;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
@@ -243,11 +244,13 @@
         }
 
         @Override
-        public void serializeWithType(JsonGenerator jgen,
-                SerializerProvider provider, TypeSerializer typeSer) throws IOException {
-            typeSer.writeTypePrefixForObject(this, jgen);
-            serialize(jgen, provider);
-            typeSer.writeTypeSuffixForObject(this, jgen);
+        public void serializeWithType(JsonGenerator g,
+                SerializerProvider provider, TypeSerializer typeSer) throws IOException
+        {
+            WritableTypeId typeIdDef = new WritableTypeId(this, JsonToken.START_OBJECT);
+            typeSer.writeTypePrefix(g, typeIdDef);
+            serialize(g, provider);
+            typeSer.writeTypePrefix(g, typeIdDef);
         }    
     }
 
diff --git a/src/test/java/com/fasterxml/jackson/databind/node/TestJsonNode.java b/src/test/java/com/fasterxml/jackson/databind/node/TestJsonNode.java
index 0dbb91f..0750fef 100644
--- a/src/test/java/com/fasterxml/jackson/databind/node/TestJsonNode.java
+++ b/src/test/java/com/fasterxml/jackson/databind/node/TestJsonNode.java
@@ -15,36 +15,7 @@
 {
     private final ObjectMapper MAPPER = objectMapper();
 
-    public void testText()
-    {
-        assertNull(TextNode.valueOf(null));
-        TextNode empty = TextNode.valueOf("");
-        assertStandardEquals(empty);
-        assertSame(TextNode.EMPTY_STRING_NODE, empty);
-
-        // 1.6:
-        assertNodeNumbers(TextNode.valueOf("-3"), -3, -3.0);
-        assertNodeNumbers(TextNode.valueOf("17.75"), 17, 17.75);
-    
-        // [JACKSON-587]
-        long value = 127353264013893L;
-        TextNode n = TextNode.valueOf(String.valueOf(value));
-        assertEquals(value, n.asLong());
-        
-        // and then with non-numeric input
-        n = TextNode.valueOf("foobar");
-        assertNodeNumbersForNonNumeric(n);
-
-        assertEquals("foobar", n.asText("barf"));
-        assertEquals("", empty.asText("xyz"));
-
-        assertTrue(TextNode.valueOf("true").asBoolean(true));
-        assertTrue(TextNode.valueOf("true").asBoolean(false));
-        assertFalse(TextNode.valueOf("false").asBoolean(true));
-        assertFalse(TextNode.valueOf("false").asBoolean(false));
-    }
-
-    public void testBoolean()
+    public void testBoolean() throws Exception
     {
         BooleanNode f = BooleanNode.getFalse();
         assertNotNull(f);
@@ -67,11 +38,23 @@
         assertEquals("true", t.asText());
         assertEquals(JsonToken.VALUE_TRUE, t.asToken());
 
-        // 1.6:
         assertNodeNumbers(f, 0, 0.0);
         assertNodeNumbers(t, 1, 1.0);
-    }
+    
+        JsonNode result = objectMapper().readTree("true\n");
+        assertFalse(result.isNull());
+        assertFalse(result.isNumber());
+        assertFalse(result.isTextual());
+        assertTrue(result.isBoolean());
+        assertType(result, BooleanNode.class);
+        assertTrue(result.booleanValue());
+        assertEquals("true", result.asText());
+        assertFalse(result.isMissingNode());
 
+        // also, equality should work ok
+        assertEquals(result, BooleanNode.valueOf(true));
+        assertEquals(result, BooleanNode.getTrue());
+    }
 
     public void testBinary() throws Exception
     {
@@ -147,6 +130,10 @@
         assertTrue(root1.equals(root1));
         assertTrue(root2.equals(root2));
 
+        assertTrue(nestedArray1.equals(nestedArray1));
+        assertFalse(nestedArray1.equals(nestedArray2));
+        assertFalse(nestedArray2.equals(nestedArray1));
+
         // but. Custom comparator can make all the difference
         Comparator<JsonNode> cmp = new Comparator<JsonNode>() {
 
@@ -159,11 +146,15 @@
                     return 0;
                 }
                 if ((o1 instanceof NumericNode) && (o2 instanceof NumericNode)) {
-                    double d1 = ((NumericNode) o1).asDouble();
-                    double d2 = ((NumericNode) o2).asDouble();
+                    int d1 = ((NumericNode) o1).asInt();
+                    int d2 = ((NumericNode) o2).asInt();
                     if (d1 == d2) { // strictly equals because it's integral value
                         return 0;
                     }
+                    if (d1 < d2) {
+                        return -1;
+                    }
+                    return 1;
                 }
                 return 0;
             }
@@ -172,6 +163,14 @@
         assertTrue(root2.equals(cmp, root1));
         assertTrue(root1.equals(cmp, root1));
         assertTrue(root2.equals(cmp, root2));
+
+        ArrayNode array3 = MAPPER.createArrayNode();
+        array3.add(123);
+        
+        assertFalse(root2.equals(cmp, nestedArray1));
+        assertTrue(nestedArray1.equals(cmp, nestedArray1));
+        assertFalse(nestedArray1.equals(cmp, root2));
+        assertFalse(nestedArray1.equals(cmp, array3));
     }
 
     // [databind#793]
diff --git a/src/test/java/com/fasterxml/jackson/databind/node/TestMissingNode.java b/src/test/java/com/fasterxml/jackson/databind/node/TestMissingNode.java
index c09d619..45f0bac 100644
--- a/src/test/java/com/fasterxml/jackson/databind/node/TestMissingNode.java
+++ b/src/test/java/com/fasterxml/jackson/databind/node/TestMissingNode.java
@@ -1,6 +1,10 @@
 package com.fasterxml.jackson.databind.node;
 
+import java.io.StringReader;
+import java.util.Iterator;
+
 import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.JsonNode;
 
 public class TestMissingNode extends NodeTestBase
 {
@@ -9,17 +13,12 @@
         MissingNode n = MissingNode.getInstance();
         assertTrue(n.isMissingNode());
         assertEquals(JsonToken.NOT_AVAILABLE, n.asToken());
-        // as per [JACKSON-775]
         assertEquals("", n.asText());
         assertStandardEquals(n);
         assertEquals("", n.toString());
 
-        /* As of 2.0, MissingNode is considered non-numeric, meaning
-         * that default values are served.
-         */
         assertNodeNumbersForNonNumeric(n);
 
-        // [JACKSON-823]
         assertTrue(n.asBoolean(true));
         assertEquals(4, n.asInt(4));
         assertEquals(5L, n.asLong(5));
@@ -27,4 +26,69 @@
 
         assertEquals("foo", n.asText("foo"));
     }
+
+    /**
+     * Let's also verify behavior of "MissingNode" -- one needs to be able
+     * to traverse such bogus nodes with appropriate methods.
+     */
+    @SuppressWarnings("unused")
+    public void testMissingViaMapper() throws Exception
+    {
+        String JSON = "[ { }, [ ] ]";
+        JsonNode result = objectMapper().readTree(new StringReader(JSON));
+
+        assertTrue(result.isContainerNode());
+        assertTrue(result.isArray());
+        assertEquals(2, result.size());
+
+        int count = 0;
+        for (JsonNode node : result) {
+            ++count;
+        }
+        assertEquals(2, count);
+
+        Iterator<JsonNode> it = result.iterator();
+
+        JsonNode onode = it.next();
+        assertTrue(onode.isContainerNode());
+        assertTrue(onode.isObject());
+        assertEquals(0, onode.size());
+        assertFalse(onode.isMissingNode()); // real node
+        assertNull(onode.textValue());
+
+        // how about dereferencing?
+        assertNull(onode.get(0));
+        JsonNode dummyNode = onode.path(0);
+        assertNotNull(dummyNode);
+        assertTrue(dummyNode.isMissingNode());
+        assertNull(dummyNode.get(3));
+        assertNull(dummyNode.get("whatever"));
+        JsonNode dummyNode2 = dummyNode.path(98);
+        assertNotNull(dummyNode2);
+        assertTrue(dummyNode2.isMissingNode());
+        JsonNode dummyNode3 = dummyNode.path("field");
+        assertNotNull(dummyNode3);
+        assertTrue(dummyNode3.isMissingNode());
+
+        // and same for the array node
+
+        JsonNode anode = it.next();
+        assertTrue(anode.isContainerNode());
+        assertTrue(anode.isArray());
+        assertFalse(anode.isMissingNode()); // real node
+        assertEquals(0, anode.size());
+
+        assertNull(anode.get(0));
+        dummyNode = anode.path(0);
+        assertNotNull(dummyNode);
+        assertTrue(dummyNode.isMissingNode());
+        assertNull(dummyNode.get(0));
+        assertNull(dummyNode.get("myfield"));
+        dummyNode2 = dummyNode.path(98);
+        assertNotNull(dummyNode2);
+        assertTrue(dummyNode2.isMissingNode());
+        dummyNode3 = dummyNode.path("f");
+        assertNotNull(dummyNode3);
+        assertTrue(dummyNode3.isMissingNode());
+    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/node/TestNullNode.java b/src/test/java/com/fasterxml/jackson/databind/node/TestNullNode.java
index c807b03..99f6eab 100644
--- a/src/test/java/com/fasterxml/jackson/databind/node/TestNullNode.java
+++ b/src/test/java/com/fasterxml/jackson/databind/node/TestNullNode.java
@@ -1,10 +1,22 @@
 package com.fasterxml.jackson.databind.node;
 
+import java.io.StringWriter;
 import java.math.BigDecimal;
 import java.math.BigInteger;
 
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
 public class TestNullNode extends NodeTestBase
 {
+    final static class CovarianceBean {
+        ObjectNode _object;
+        ArrayNode _array;
+
+        public void setObject(ObjectNode n) { _object = n; }
+        public void setArray(ArrayNode n) { _array = n; }
+    }
+
     public void testBasicsWithNullNode() throws Exception
     {
         // Let's use something that doesn't add much beyond JsonNode base
@@ -42,4 +54,47 @@
         // 2.4
         assertEquals("foo", n.asText("foo"));
     }
+
+    public void testNullHandling() throws Exception
+    {
+        // First, a stand-alone null
+        JsonNode n = objectReader().readTree("null");
+        assertNotNull(n);
+        assertTrue(n.isNull());
+        assertFalse(n.isNumber());
+        assertFalse(n.isTextual());
+        assertEquals("null", n.asText());
+        assertEquals(n, NullNode.instance);
+
+        n = objectMapper().readTree("null");
+        assertNotNull(n);
+        assertTrue(n.isNull());
+        
+        // Then object property
+        ObjectNode root = (ObjectNode) objectReader().readTree("{\"x\":null}");
+        assertEquals(1, root.size());
+        n = root.get("x");
+        assertNotNull(n);
+        assertTrue(n.isNull());
+    }
+
+    public void testNullSerialization() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        StringWriter sw = new StringWriter();
+        mapper.writeValue(sw, NullNode.instance);
+        assertEquals("null", sw.toString());
+    }
+
+    public void testNullHandlingCovariance() throws Exception
+    {
+        String JSON = "{\"object\" : null, \"array\" : null }";
+        CovarianceBean bean = objectMapper().readValue(JSON, CovarianceBean.class);
+
+        ObjectNode on = bean._object;
+        assertNull(on);
+
+        ArrayNode an = bean._array;
+        assertNull(an);
+    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/node/TestTreeDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/node/TestTreeDeserialization.java
index 5435b6d..5bf8d85 100644
--- a/src/test/java/com/fasterxml/jackson/databind/node/TestTreeDeserialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/node/TestTreeDeserialization.java
@@ -3,7 +3,6 @@
 import com.fasterxml.jackson.databind.BaseMapTest;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
-import java.io.IOException;
 
 /**
  * This unit test suite tries to verify that JsonNode-based trees
@@ -20,58 +19,12 @@
         public void setNode(JsonNode n) { _node = n; }
     }
 
-    final static class CovarianceBean {
-        ObjectNode _object;
-        ArrayNode _array;
-
-        public void setObject(ObjectNode n) { _object = n; }
-        public void setArray(ArrayNode n) { _array = n; }
-    }
-
     /*
     /**********************************************************
     /* Unit tests
     /**********************************************************
      */
 
-    /**
-     * This test checks that is possible to mix "regular" Java objects
-     * and JsonNode.
-     */
-    public void testMixed() throws IOException
-    {
-        ObjectMapper om = new ObjectMapper();
-        String JSON = "{\"node\" : { \"a\" : 3 }, \"x\" : 9 }";
-        Bean bean = om.readValue(JSON, Bean.class);
-
-        assertEquals(9, bean._x);
-        JsonNode n = bean._node;
-        assertNotNull(n);
-        assertEquals(1, n.size());
-        ObjectNode on = (ObjectNode) n;
-        assertEquals(3, on.get("a").intValue());
-    }
-
-    /// Verifying [JACKSON-143]
-    public void testArrayNodeEquality()
-    {
-        ArrayNode n1 = new ArrayNode(null);
-        ArrayNode n2 = new ArrayNode(null);
-
-        assertTrue(n1.equals(n2));
-        assertTrue(n2.equals(n1));
-
-        n1.add(TextNode.valueOf("Test"));
-
-        assertFalse(n1.equals(n2));
-        assertFalse(n2.equals(n1));
-
-        n2.add(TextNode.valueOf("Test"));
-
-        assertTrue(n1.equals(n2));
-        assertTrue(n2.equals(n1));
-    }
-
     public void testObjectNodeEquality()
     {
         ObjectNode n1 = new ObjectNode(null);
@@ -104,36 +57,4 @@
         String value = out.path("field").asText();
         assertNotNull(value);
     }
-
-    // [databind#186]
-    public void testNullHandling() throws Exception
-    {
-        // First, a stand-alone null
-        JsonNode n = objectReader().readTree("null");
-        assertNotNull(n);
-        assertTrue(n.isNull());
-
-        n = objectMapper().readTree("null");
-        assertNotNull(n);
-        assertTrue(n.isNull());
-        
-        // Then object property
-        ObjectNode root = (ObjectNode) objectReader().readTree("{\"x\":null}");
-        assertEquals(1, root.size());
-        n = root.get("x");
-        assertNotNull(n);
-        assertTrue(n.isNull());
-    }
-
-    public void testNullHandlingCovariance() throws Exception
-    {
-        String JSON = "{\"object\" : null, \"array\" : null }";
-        CovarianceBean bean = objectMapper().readValue(JSON, CovarianceBean.class);
-
-        ObjectNode on = bean._object;
-        assertNull(on);
-
-        ArrayNode an = bean._array;
-        assertNull(an);
-    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/node/TestTreeMapperDeserializer.java b/src/test/java/com/fasterxml/jackson/databind/node/TestTreeMapperDeserializer.java
deleted file mode 100644
index 37410aa..0000000
--- a/src/test/java/com/fasterxml/jackson/databind/node/TestTreeMapperDeserializer.java
+++ /dev/null
@@ -1,425 +0,0 @@
-package com.fasterxml.jackson.databind.node;
-
-import java.io.*;
-import java.math.BigDecimal;
-import java.util.*;
-
-import com.fasterxml.jackson.core.*;
-import com.fasterxml.jackson.databind.*;
-
-/**
- * This unit test suite tries to verify that ObjectMapper
- * can properly parse JSON and bind contents into appropriate
- * JsonNode instances.
- */
-public class TestTreeMapperDeserializer extends BaseMapTest
-{
-	public void testSimple()
-        throws Exception
-    {
-        final String JSON = SAMPLE_DOC_JSON_SPEC;
-
-        for (int type = 0; type < 2; ++type) {
-            JsonNode result;
-
-            if (type == 0) {
-                result = objectMapper().readTree(new StringReader(JSON));
-            } else {
-                result = objectMapper().readTree(JSON);
-            }
-
-            assertType(result, ObjectNode.class);
-            assertEquals(1, result.size());
-            assertTrue(result.isObject());
-            
-            ObjectNode main = (ObjectNode) result;
-            assertEquals("Image", main.fieldNames().next());
-            JsonNode ob = main.elements().next();
-            assertType(ob, ObjectNode.class);
-            ObjectNode imageMap = (ObjectNode) ob;
-            
-            assertEquals(5, imageMap.size());
-            ob = imageMap.get("Width");
-            assertTrue(ob.isIntegralNumber());
-            assertFalse(ob.isFloatingPointNumber());
-            assertEquals(SAMPLE_SPEC_VALUE_WIDTH, ob.intValue());
-            ob = imageMap.get("Height");
-            assertTrue(ob.isIntegralNumber());
-            assertEquals(SAMPLE_SPEC_VALUE_HEIGHT, ob.intValue());
-            
-            ob = imageMap.get("Title");
-            assertTrue(ob.isTextual());
-            assertEquals(SAMPLE_SPEC_VALUE_TITLE, ob.textValue());
-            
-            ob = imageMap.get("Thumbnail");
-            assertType(ob, ObjectNode.class);
-            ObjectNode tn = (ObjectNode) ob;
-            ob = tn.get("Url");
-            assertTrue(ob.isTextual());
-            assertEquals(SAMPLE_SPEC_VALUE_TN_URL, ob.textValue());
-            ob = tn.get("Height");
-            assertTrue(ob.isIntegralNumber());
-            assertEquals(SAMPLE_SPEC_VALUE_TN_HEIGHT, ob.intValue());
-            ob = tn.get("Width");
-            assertTrue(ob.isTextual());
-            assertEquals(SAMPLE_SPEC_VALUE_TN_WIDTH, ob.textValue());
-            
-            ob = imageMap.get("IDs");
-            assertTrue(ob.isArray());
-            ArrayNode idList = (ArrayNode) ob;
-            assertEquals(4, idList.size());
-            assertEquals(4, calcLength(idList.elements()));
-            assertEquals(4, calcLength(idList.iterator()));
-            {
-                int[] values = new int[] {
-                    SAMPLE_SPEC_VALUE_TN_ID1,
-                    SAMPLE_SPEC_VALUE_TN_ID2,
-                    SAMPLE_SPEC_VALUE_TN_ID3,
-                    SAMPLE_SPEC_VALUE_TN_ID4
-                };
-                for (int i = 0; i < values.length; ++i) {
-                    assertEquals(values[i], idList.get(i).intValue());
-                }
-                int i = 0;
-                for (JsonNode n : idList) {
-                    assertEquals(values[i], n.intValue());
-                    ++i;
-                }
-            }
-        }
-    }
-
-    public void testBoolean()
-        throws Exception
-    {
-        JsonNode result = objectMapper().readTree("true\n");
-        assertFalse(result.isNull());
-        assertFalse(result.isNumber());
-        assertFalse(result.isTextual());
-        assertTrue(result.isBoolean());
-        assertType(result, BooleanNode.class);
-        assertTrue(result.booleanValue());
-        assertEquals("true", result.asText());
-        assertFalse(result.isMissingNode());
-
-        // also, equality should work ok
-        assertEquals(result, BooleanNode.valueOf(true));
-        assertEquals(result, BooleanNode.getTrue());
-    }
-
-    public void testDouble()
-        throws Exception
-    {
-        double value = 3.04;
-        JsonNode result = objectMapper().readTree(String.valueOf(value));
-        assertTrue(result.isNumber());
-        assertFalse(result.isNull());
-        assertType(result, DoubleNode.class);
-        assertTrue(result.isFloatingPointNumber());
-        assertTrue(result.isDouble());
-        assertFalse(result.isInt());
-        assertFalse(result.isLong());
-        assertFalse(result.isIntegralNumber());
-        assertFalse(result.isTextual());
-        assertFalse(result.isMissingNode());
-
-        assertEquals(value, result.doubleValue());
-        assertEquals(value, result.numberValue().doubleValue());
-        assertEquals((int) value, result.intValue());
-        assertEquals((long) value, result.longValue());
-        assertEquals(String.valueOf(value), result.asText());
-
-        // also, equality should work ok
-        assertEquals(result, DoubleNode.valueOf(value));
-    }
-
-    public void testInt()
-        throws Exception
-    {
-        int value = -90184;
-        JsonNode result = objectMapper().readTree(String.valueOf(value));
-        assertTrue(result.isNumber());
-        assertTrue(result.isIntegralNumber());
-        assertTrue(result.isInt());
-        assertType(result, IntNode.class);
-        assertFalse(result.isLong());
-        assertFalse(result.isFloatingPointNumber());
-        assertFalse(result.isDouble());
-        assertFalse(result.isNull());
-        assertFalse(result.isTextual());
-        assertFalse(result.isMissingNode());
-
-        assertEquals(value, result.numberValue().intValue());
-        assertEquals(value, result.intValue());
-        assertEquals(String.valueOf(value), result.asText());
-        assertEquals((double) value, result.doubleValue());
-        assertEquals((long) value, result.longValue());
-
-        // also, equality should work ok
-        assertEquals(result, IntNode.valueOf(value));
-    }
-
-    public void testLong() throws Exception
-    {
-        // need to use something being 32-bit value space
-        long value = 12345678L << 32;
-        JsonNode result = objectMapper().readTree(String.valueOf(value));
-        assertTrue(result.isNumber());
-        assertTrue(result.isIntegralNumber());
-        assertTrue(result.isLong());
-        assertType(result, LongNode.class);
-        assertFalse(result.isInt());
-        assertFalse(result.isFloatingPointNumber());
-        assertFalse(result.isDouble());
-        assertFalse(result.isNull());
-        assertFalse(result.isTextual());
-        assertFalse(result.isMissingNode());
-
-        assertEquals(value, result.numberValue().longValue());
-        assertEquals(value, result.longValue());
-        assertEquals(String.valueOf(value), result.asText());
-        assertEquals((double) value, result.doubleValue());
-
-        // also, equality should work ok
-        assertEquals(result, LongNode.valueOf(value));
-    }
-
-    public void testNull() throws Exception
-    {
-        JsonNode result = objectMapper().readTree("   null ");
-        // should not get java null, but NullNode...
-        assertNotNull(result);
-        assertTrue(result.isNull());
-        assertFalse(result.isNumber());
-        assertFalse(result.isTextual());
-        assertEquals("null", result.asText());
-
-        // also, equality should work ok
-        assertEquals(result, NullNode.instance);
-    }
-
-    public void testDecimalNode()
-        throws Exception
-    {
-        // no "natural" way to get it, must construct
-        BigDecimal value = new BigDecimal("0.1");
-        JsonNode result = DecimalNode.valueOf(value);
-
-        assertFalse(result.isArray());
-        assertFalse(result.isObject());
-        assertTrue(result.isNumber());
-        assertFalse(result.isIntegralNumber());
-        assertFalse(result.isLong());
-        assertType(result, DecimalNode.class);
-        assertFalse(result.isInt());
-        assertTrue(result.isFloatingPointNumber());
-        assertTrue(result.isBigDecimal());
-        assertFalse(result.isDouble());
-        assertFalse(result.isNull());
-        assertFalse(result.isTextual());
-        assertFalse(result.isMissingNode());
-
-        assertEquals(value, result.numberValue());
-        assertEquals(value.toString(), result.asText());
-
-        // also, equality should work ok
-        assertEquals(result, DecimalNode.valueOf(value));
-    }
-
-    public void testSimpleArray() throws Exception
-    {
-        ArrayNode result = objectMapper().createArrayNode();
-
-        assertTrue(result.isArray());
-        assertType(result, ArrayNode.class);
-
-        assertFalse(result.isObject());
-        assertFalse(result.isNumber());
-        assertFalse(result.isNull());
-        assertFalse(result.isTextual());
-
-        // and let's add stuff...
-        result.add(false);
-        result.insertNull(0);
-
-        // should be equal to itself no matter what
-        assertEquals(result, result);
-        assertFalse(result.equals(null)); // but not to null
-
-        // plus see that we can access stuff
-        assertEquals(NullNode.instance, result.path(0));
-        assertEquals(NullNode.instance, result.get(0));
-        assertEquals(BooleanNode.FALSE, result.path(1));
-        assertEquals(BooleanNode.FALSE, result.get(1));
-        assertEquals(2, result.size());
-
-        assertNull(result.get(-1));
-        assertNull(result.get(2));
-        JsonNode missing = result.path(2);
-        assertTrue(missing.isMissingNode());
-        assertTrue(result.path(-100).isMissingNode());
-
-        // then construct and compare
-        ArrayNode array2 = objectMapper().createArrayNode();
-        array2.addNull();
-        array2.add(false);
-        assertEquals(result, array2);
-
-        // plus remove entries
-        JsonNode rm1 = array2.remove(0);
-        assertEquals(NullNode.instance, rm1);
-        assertEquals(1, array2.size());
-        assertEquals(BooleanNode.FALSE, array2.get(0));
-        assertFalse(result.equals(array2));
-
-        JsonNode rm2 = array2.remove(0);
-        assertEquals(BooleanNode.FALSE, rm2);
-        assertEquals(0, array2.size());
-    }
-
-    /**
-     * Type mappers should be able to gracefully deal with end of
-     * input.
-     */
-    public void testEOF() throws Exception
-    {
-        String JSON =
-            "{ \"key\": [ { \"a\" : { \"name\": \"foo\",  \"type\": 1\n"
-            +"},  \"type\": 3, \"url\": \"http://www.google.com\" } ],\n"
-            +"\"name\": \"xyz\", \"type\": 1, \"url\" : null }\n  "
-            ;
-        JsonFactory jf = new JsonFactory();
-        JsonParser jp = jf.createParser(new StringReader(JSON));
-        JsonNode result = objectMapper().readTree(jp);
-
-        assertTrue(result.isObject());
-        assertEquals(4, result.size());
-
-        assertNull(objectMapper().readTree(jp));
-        jp.close();
-    }
-
-    public void testMultiple() throws Exception
-    {
-        String JSON = "12  \"string\" [ 1, 2, 3 ]";
-        JsonFactory jf = new JsonFactory();
-        JsonParser jp = jf.createParser(new StringReader(JSON));
-        final ObjectMapper mapper = objectMapper();
-        JsonNode result = mapper.readTree(jp);
-
-        assertTrue(result.isIntegralNumber());
-        assertTrue(result.isInt());
-        assertFalse(result.isTextual());
-        assertEquals(12, result.intValue());
-
-        result = mapper.readTree(jp);
-        assertTrue(result.isTextual());
-        assertFalse(result.isIntegralNumber());
-        assertFalse(result.isInt());
-        assertEquals("string", result.textValue());
-
-        result = mapper.readTree(jp);
-        assertTrue(result.isArray());
-        assertEquals(3, result.size());
-
-        assertNull(mapper.readTree(jp));
-        jp.close();
-    }
-
-    /**
-     * Let's also verify behavior of "MissingNode" -- one needs to be able
-     * to traverse such bogus nodes with appropriate methods.
-     */
-    @SuppressWarnings("unused")
-    public void testMissingNode()
-        throws Exception
-    {
-        String JSON = "[ { }, [ ] ]";
-        JsonNode result = objectMapper().readTree(new StringReader(JSON));
-
-        assertTrue(result.isContainerNode());
-        assertTrue(result.isArray());
-        assertEquals(2, result.size());
-
-        int count = 0;
-        for (JsonNode node : result) {
-            ++count;
-        }
-        assertEquals(2, count);
-
-        Iterator<JsonNode> it = result.iterator();
-
-        JsonNode onode = it.next();
-        assertTrue(onode.isContainerNode());
-        assertTrue(onode.isObject());
-        assertEquals(0, onode.size());
-        assertFalse(onode.isMissingNode()); // real node
-        assertNull(onode.textValue());
-
-        // how about dereferencing?
-        assertNull(onode.get(0));
-        JsonNode dummyNode = onode.path(0);
-        assertNotNull(dummyNode);
-        assertTrue(dummyNode.isMissingNode());
-        assertNull(dummyNode.get(3));
-        assertNull(dummyNode.get("whatever"));
-        JsonNode dummyNode2 = dummyNode.path(98);
-        assertNotNull(dummyNode2);
-        assertTrue(dummyNode2.isMissingNode());
-        JsonNode dummyNode3 = dummyNode.path("field");
-        assertNotNull(dummyNode3);
-        assertTrue(dummyNode3.isMissingNode());
-
-        // and same for the array node
-
-        JsonNode anode = it.next();
-        assertTrue(anode.isContainerNode());
-        assertTrue(anode.isArray());
-        assertFalse(anode.isMissingNode()); // real node
-        assertEquals(0, anode.size());
-
-        assertNull(anode.get(0));
-        dummyNode = anode.path(0);
-        assertNotNull(dummyNode);
-        assertTrue(dummyNode.isMissingNode());
-        assertNull(dummyNode.get(0));
-        assertNull(dummyNode.get("myfield"));
-        dummyNode2 = dummyNode.path(98);
-        assertNotNull(dummyNode2);
-        assertTrue(dummyNode2.isMissingNode());
-        dummyNode3 = dummyNode.path("f");
-        assertNotNull(dummyNode3);
-        assertTrue(dummyNode3.isMissingNode());
-    }
-
-    public void testArray() throws Exception
-    {
-        final String JSON = "[[[-0.027512,51.503221],[-0.008497,51.503221],[-0.008497,51.509744],[-0.027512,51.509744]]]";
-
-        JsonNode n = objectMapper().readTree(JSON);
-        assertNotNull(n);
-        assertTrue(n.isArray());
-        ArrayNode an = (ArrayNode) n;
-        assertEquals(1, an.size());
-        ArrayNode an2 = (ArrayNode) n.get(0);
-        assertTrue(an2.isArray());
-        assertEquals(4, an2.size());
-    }
-    
-    /*
-    /**********************************************
-    /* Helper methods
-    /**********************************************
-     */
-
-    private int calcLength(Iterator<JsonNode> it)
-    {
-        int count = 0;
-        while (it.hasNext()) {
-            it.next();
-            ++count;
-        }
-        return count;
-    }
-}
-
diff --git a/src/test/java/com/fasterxml/jackson/databind/node/TestTreeMapperSerializer.java b/src/test/java/com/fasterxml/jackson/databind/node/TestTreeMapperSerializer.java
index 6114ed6..10f77a8 100644
--- a/src/test/java/com/fasterxml/jackson/databind/node/TestTreeMapperSerializer.java
+++ b/src/test/java/com/fasterxml/jackson/databind/node/TestTreeMapperSerializer.java
@@ -11,8 +11,7 @@
  * This unit test suite tries to verify that the trees ObjectMapper
  * constructs can be serialized properly.
  */
-public class TestTreeMapperSerializer
-    extends BaseMapTest
+public class TestTreeMapperSerializer extends NodeTestBase
 {
     final static String FIELD1 = "first";
     final static String FIELD2 = "Second?";
@@ -24,8 +23,7 @@
 
     final static double DOUBLE_VALUE = 9.25;
 
-    public void testFromArray()
-        throws Exception
+    public void testFromArray() throws Exception
     {
         ObjectMapper mapper = new ObjectMapper();
         ArrayNode root = mapper.createArrayNode();
@@ -113,29 +111,20 @@
             }
             
             String doc = sw.toString();
-            JsonParser jp = new JsonFactory().createParser(new StringReader(doc));
+            JsonParser p = new JsonFactory().createParser(new StringReader(doc));
             
-            assertEquals(JsonToken.START_ARRAY, jp.nextToken());
+            assertEquals(JsonToken.START_ARRAY, p.nextToken());
             for (int i = -20; i <= 20; ++i) {
-                assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
-                assertEquals(i, jp.getIntValue());
-                assertEquals(""+i, jp.getText());
+                assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+                assertEquals(i, p.getIntValue());
+                assertEquals(""+i, p.getText());
             }
-            assertEquals(JsonToken.END_ARRAY, jp.nextToken());
-            jp.close();
+            assertEquals(JsonToken.END_ARRAY, p.nextToken());
+            p.close();
         }
     }
 
-    public void testNull() throws Exception
-    {
-        ObjectMapper mapper = new ObjectMapper();
-        StringWriter sw = new StringWriter();
-        mapper.writeValue(sw, NullNode.instance);
-        assertEquals("null", sw.toString());
-    }
-
-    public void testBinary()
-        throws Exception
+    public void testBinary() throws Exception
     {
         ObjectMapper mapper = new ObjectMapper();
         final int LENGTH = 13045;
@@ -146,11 +135,11 @@
         StringWriter sw = new StringWriter();
         mapper.writeValue(sw, BinaryNode.valueOf(data));
 
-        JsonParser jp = new JsonFactory().createParser(sw.toString());
+        JsonParser p = new JsonFactory().createParser(sw.toString());
         // note: can't determine it's binary from json alone:
-        assertToken(JsonToken.VALUE_STRING, jp.nextToken());
-        assertArrayEquals(data, jp.getBinaryValue());
-        jp.close();
+        assertToken(JsonToken.VALUE_STRING, p.nextToken());
+        assertArrayEquals(data, p.getBinaryValue());
+        p.close();
     }
 
     /*
@@ -162,63 +151,63 @@
     private void verifyFromArray(String input)
         throws Exception
     {
-        JsonParser jp = new JsonFactory().createParser(new StringReader(input));
+        JsonParser p = new JsonFactory().createParser(new StringReader(input));
         
-        assertEquals(JsonToken.START_ARRAY, jp.nextToken());
+        assertEquals(JsonToken.START_ARRAY, p.nextToken());
         
-        assertEquals(JsonToken.VALUE_STRING, jp.nextToken());
-        assertEquals(TEXT1, getAndVerifyText(jp));
+        assertEquals(JsonToken.VALUE_STRING, p.nextToken());
+        assertEquals(TEXT1, getAndVerifyText(p));
         
-        assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
-        assertEquals(3, jp.getIntValue());
+        assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+        assertEquals(3, p.getIntValue());
         
-        assertEquals(JsonToken.START_OBJECT, jp.nextToken());
-        assertEquals(JsonToken.FIELD_NAME, jp.nextToken());
-        assertEquals(FIELD1, getAndVerifyText(jp));
+        assertEquals(JsonToken.START_OBJECT, p.nextToken());
+        assertEquals(JsonToken.FIELD_NAME, p.nextToken());
+        assertEquals(FIELD1, getAndVerifyText(p));
         
-        assertEquals(JsonToken.VALUE_TRUE, jp.nextToken());
-        assertEquals(JsonToken.FIELD_NAME, jp.nextToken());
-        assertEquals(FIELD2, getAndVerifyText(jp));
+        assertEquals(JsonToken.VALUE_TRUE, p.nextToken());
+        assertEquals(JsonToken.FIELD_NAME, p.nextToken());
+        assertEquals(FIELD2, getAndVerifyText(p));
         
-        assertEquals(JsonToken.START_ARRAY, jp.nextToken());
-        assertEquals(JsonToken.END_ARRAY, jp.nextToken());
-        assertEquals(JsonToken.END_OBJECT, jp.nextToken());
+        assertEquals(JsonToken.START_ARRAY, p.nextToken());
+        assertEquals(JsonToken.END_ARRAY, p.nextToken());
+        assertEquals(JsonToken.END_OBJECT, p.nextToken());
         
-        assertEquals(JsonToken.VALUE_FALSE, jp.nextToken());
+        assertEquals(JsonToken.VALUE_FALSE, p.nextToken());
         
-        assertEquals(JsonToken.END_ARRAY, jp.nextToken());
-        assertNull(jp.nextToken());
-        jp.close();
+        assertEquals(JsonToken.END_ARRAY, p.nextToken());
+        assertNull(p.nextToken());
+        p.close();
     }
 
     private void verifyFromMap(String input)
         throws Exception
     {
-        JsonParser jp = new JsonFactory().createParser(new StringReader(input));
-        assertEquals(JsonToken.START_OBJECT, jp.nextToken());
-        assertEquals(JsonToken.FIELD_NAME, jp.nextToken());
-        assertEquals(FIELD4, getAndVerifyText(jp));
-        assertEquals(JsonToken.VALUE_STRING, jp.nextToken());
-        assertEquals(TEXT2, getAndVerifyText(jp));
+        JsonParser p = new JsonFactory().createParser(input);
+        assertEquals(JsonToken.START_OBJECT, p.nextToken());
+        assertEquals(JsonToken.FIELD_NAME, p.nextToken());
+        assertEquals(FIELD4, getAndVerifyText(p));
+        assertEquals(JsonToken.VALUE_STRING, p.nextToken());
+        assertEquals(TEXT2, getAndVerifyText(p));
         
-        assertEquals(JsonToken.FIELD_NAME, jp.nextToken());
-        assertEquals(FIELD3, getAndVerifyText(jp));
-        assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
-        assertEquals(-1, jp.getIntValue());
+        assertEquals(JsonToken.FIELD_NAME, p.nextToken());
+        assertEquals(FIELD3, getAndVerifyText(p));
+        assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+        assertEquals(-1, p.getIntValue());
         
-        assertEquals(JsonToken.FIELD_NAME, jp.nextToken());
-        assertEquals(FIELD2, getAndVerifyText(jp));
-        assertEquals(JsonToken.START_ARRAY, jp.nextToken());
-        assertEquals(JsonToken.END_ARRAY, jp.nextToken());
+        assertEquals(JsonToken.FIELD_NAME, p.nextToken());
+        assertEquals(FIELD2, getAndVerifyText(p));
+        assertEquals(JsonToken.START_ARRAY, p.nextToken());
+        assertEquals(JsonToken.END_ARRAY, p.nextToken());
         
-        assertEquals(JsonToken.FIELD_NAME, jp.nextToken());
-        assertEquals(FIELD1, getAndVerifyText(jp));
-        assertEquals(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
-        assertEquals(DOUBLE_VALUE, jp.getDoubleValue(), 0);
+        assertEquals(JsonToken.FIELD_NAME, p.nextToken());
+        assertEquals(FIELD1, getAndVerifyText(p));
+        assertEquals(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
+        assertEquals(DOUBLE_VALUE, p.getDoubleValue(), 0);
         
-        assertEquals(JsonToken.END_OBJECT, jp.nextToken());
+        assertEquals(JsonToken.END_OBJECT, p.nextToken());
         
-        assertNull(jp.nextToken());
-        jp.close();
+        assertNull(p.nextToken());
+        p.close();
     }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/node/TestTreeTraversingParser.java b/src/test/java/com/fasterxml/jackson/databind/node/TestTreeTraversingParser.java
index f0aa428..0573696 100644
--- a/src/test/java/com/fasterxml/jackson/databind/node/TestTreeTraversingParser.java
+++ b/src/test/java/com/fasterxml/jackson/databind/node/TestTreeTraversingParser.java
@@ -8,6 +8,7 @@
 
 import com.fasterxml.jackson.core.*;
 import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.exc.InvalidFormatException;
 import com.fasterxml.jackson.databind.node.BinaryNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.fasterxml.jackson.databind.node.POJONode;
@@ -22,7 +23,6 @@
         public List<String> kids;
     }
 
-    // Helper class for [JACKSON-370]
     @JsonIgnoreProperties(ignoreUnknown=true)
     public static class Jackson370Bean {
         public Inner inner;
@@ -45,65 +45,65 @@
             "{ \"a\" : 123, \"list\" : [ 12.25, null, true, { }, [ ] ] }";
         ObjectMapper m = new ObjectMapper();
         JsonNode tree = m.readTree(JSON);
-        JsonParser jp = tree.traverse();
+        JsonParser p = tree.traverse();
 
-        assertNull(jp.getCurrentToken());
-        assertNull(jp.getCurrentName());
+        assertNull(p.getCurrentToken());
+        assertNull(p.getCurrentName());
 
-        assertToken(JsonToken.START_OBJECT, jp.nextToken());
-        assertNull(jp.getCurrentName());
-        assertEquals("Expected START_OBJECT", JsonToken.START_OBJECT.asString(), jp.getText());
+        assertToken(JsonToken.START_OBJECT, p.nextToken());
+        assertNull(p.getCurrentName());
+        assertEquals("Expected START_OBJECT", JsonToken.START_OBJECT.asString(), p.getText());
 
-        assertToken(JsonToken.FIELD_NAME, jp.nextToken());
-        assertEquals("a", jp.getCurrentName());
-        assertEquals("a", jp.getText());
+        assertToken(JsonToken.FIELD_NAME, p.nextToken());
+        assertEquals("a", p.getCurrentName());
+        assertEquals("a", p.getText());
 
-        assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
-        assertEquals("a", jp.getCurrentName());
-        assertEquals(123, jp.getIntValue());
-        assertEquals("123", jp.getText());
+        assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+        assertEquals("a", p.getCurrentName());
+        assertEquals(123, p.getIntValue());
+        assertEquals("123", p.getText());
 
-        assertToken(JsonToken.FIELD_NAME, jp.nextToken());
-        assertEquals("list", jp.getCurrentName());
-        assertEquals("list", jp.getText());
+        assertToken(JsonToken.FIELD_NAME, p.nextToken());
+        assertEquals("list", p.getCurrentName());
+        assertEquals("list", p.getText());
 
-        assertToken(JsonToken.START_ARRAY, jp.nextToken());
-        assertEquals("list", jp.getCurrentName());
-        assertEquals(JsonToken.START_ARRAY.asString(), jp.getText());
+        assertToken(JsonToken.START_ARRAY, p.nextToken());
+        assertEquals("list", p.getCurrentName());
+        assertEquals(JsonToken.START_ARRAY.asString(), p.getText());
 
-        assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
-        assertNull(jp.getCurrentName());
-        assertEquals(12.25, jp.getDoubleValue(), 0);
-        assertEquals("12.25", jp.getText());
+        assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
+        assertNull(p.getCurrentName());
+        assertEquals(12.25, p.getDoubleValue(), 0);
+        assertEquals("12.25", p.getText());
 
-        assertToken(JsonToken.VALUE_NULL, jp.nextToken());
-        assertNull(jp.getCurrentName());
-        assertEquals(JsonToken.VALUE_NULL.asString(), jp.getText());
+        assertToken(JsonToken.VALUE_NULL, p.nextToken());
+        assertNull(p.getCurrentName());
+        assertEquals(JsonToken.VALUE_NULL.asString(), p.getText());
 
-        assertToken(JsonToken.VALUE_TRUE, jp.nextToken());
-        assertNull(jp.getCurrentName());
-        assertTrue(jp.getBooleanValue());
-        assertEquals(JsonToken.VALUE_TRUE.asString(), jp.getText());
+        assertToken(JsonToken.VALUE_TRUE, p.nextToken());
+        assertNull(p.getCurrentName());
+        assertTrue(p.getBooleanValue());
+        assertEquals(JsonToken.VALUE_TRUE.asString(), p.getText());
 
-        assertToken(JsonToken.START_OBJECT, jp.nextToken());
-        assertNull(jp.getCurrentName());
-        assertToken(JsonToken.END_OBJECT, jp.nextToken());
-        assertNull(jp.getCurrentName());
+        assertToken(JsonToken.START_OBJECT, p.nextToken());
+        assertNull(p.getCurrentName());
+        assertToken(JsonToken.END_OBJECT, p.nextToken());
+        assertNull(p.getCurrentName());
 
-        assertToken(JsonToken.START_ARRAY, jp.nextToken());
-        assertNull(jp.getCurrentName());
-        assertToken(JsonToken.END_ARRAY, jp.nextToken());
-        assertNull(jp.getCurrentName());
+        assertToken(JsonToken.START_ARRAY, p.nextToken());
+        assertNull(p.getCurrentName());
+        assertToken(JsonToken.END_ARRAY, p.nextToken());
+        assertNull(p.getCurrentName());
 
-        assertToken(JsonToken.END_ARRAY, jp.nextToken());
+        assertToken(JsonToken.END_ARRAY, p.nextToken());
 
-        assertToken(JsonToken.END_OBJECT, jp.nextToken());
-        assertNull(jp.getCurrentName());
+        assertToken(JsonToken.END_OBJECT, p.nextToken());
+        assertNull(p.getCurrentName());
 
-        assertNull(jp.nextToken());
+        assertNull(p.nextToken());
 
-        jp.close();
-        assertTrue(jp.isClosed());
+        p.close();
+        assertTrue(p.isClosed());
     }
 
     public void testArray() throws Exception
@@ -111,25 +111,25 @@
         // For convenience, parse tree from JSON first
         ObjectMapper m = new ObjectMapper();
 
-        JsonParser jp = m.readTree("[]").traverse();
-        assertToken(JsonToken.START_ARRAY, jp.nextToken());
-        assertToken(JsonToken.END_ARRAY, jp.nextToken());
-        jp.close();
+        JsonParser p = m.readTree("[]").traverse();
+        assertToken(JsonToken.START_ARRAY, p.nextToken());
+        assertToken(JsonToken.END_ARRAY, p.nextToken());
+        p.close();
 
-        jp = m.readTree("[[]]").traverse();
-        assertToken(JsonToken.START_ARRAY, jp.nextToken());
-        assertToken(JsonToken.START_ARRAY, jp.nextToken());
-        assertToken(JsonToken.END_ARRAY, jp.nextToken());
-        assertToken(JsonToken.END_ARRAY, jp.nextToken());
-        jp.close();
+        p = m.readTree("[[]]").traverse();
+        assertToken(JsonToken.START_ARRAY, p.nextToken());
+        assertToken(JsonToken.START_ARRAY, p.nextToken());
+        assertToken(JsonToken.END_ARRAY, p.nextToken());
+        assertToken(JsonToken.END_ARRAY, p.nextToken());
+        p.close();
 
-        jp = m.readTree("[[ 12.1 ]]").traverse();
-        assertToken(JsonToken.START_ARRAY, jp.nextToken());
-        assertToken(JsonToken.START_ARRAY, jp.nextToken());
-        assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
-        assertToken(JsonToken.END_ARRAY, jp.nextToken());
-        assertToken(JsonToken.END_ARRAY, jp.nextToken());
-        jp.close();
+        p = m.readTree("[[ 12.1 ]]").traverse();
+        assertToken(JsonToken.START_ARRAY, p.nextToken());
+        assertToken(JsonToken.START_ARRAY, p.nextToken());
+        assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
+        assertToken(JsonToken.END_ARRAY, p.nextToken());
+        assertToken(JsonToken.END_ARRAY, p.nextToken());
+        p.close();
     }
     
     public void testNested() throws Exception
@@ -140,28 +140,28 @@
             ;
         ObjectMapper m = new ObjectMapper();
         JsonNode tree = m.readTree(JSON);
-        JsonParser jp = tree.traverse();
-        assertToken(JsonToken.START_OBJECT, jp.nextToken());
-        assertToken(JsonToken.FIELD_NAME, jp.nextToken());
+        JsonParser p = tree.traverse();
+        assertToken(JsonToken.START_OBJECT, p.nextToken());
+        assertToken(JsonToken.FIELD_NAME, p.nextToken());
 
-        assertToken(JsonToken.START_ARRAY, jp.nextToken());
-        assertToken(JsonToken.START_ARRAY, jp.nextToken());
+        assertToken(JsonToken.START_ARRAY, p.nextToken());
+        assertToken(JsonToken.START_ARRAY, p.nextToken());
 
-        assertToken(JsonToken.START_ARRAY, jp.nextToken());
-        assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
-        assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
-        assertToken(JsonToken.END_ARRAY, jp.nextToken());
+        assertToken(JsonToken.START_ARRAY, p.nextToken());
+        assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+        assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+        assertToken(JsonToken.END_ARRAY, p.nextToken());
 
-        assertToken(JsonToken.START_ARRAY, jp.nextToken());
-        assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
-        assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
-        assertToken(JsonToken.END_ARRAY, jp.nextToken());
+        assertToken(JsonToken.START_ARRAY, p.nextToken());
+        assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
+        assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
+        assertToken(JsonToken.END_ARRAY, p.nextToken());
         
-        assertToken(JsonToken.END_ARRAY, jp.nextToken());
-        assertToken(JsonToken.END_ARRAY, jp.nextToken());
+        assertToken(JsonToken.END_ARRAY, p.nextToken());
+        assertToken(JsonToken.END_ARRAY, p.nextToken());
 
-        assertToken(JsonToken.END_OBJECT, jp.nextToken());
-        jp.close();
+        assertToken(JsonToken.END_OBJECT, p.nextToken());
+        p.close();
     }
     
     /**
@@ -172,71 +172,71 @@
     {
         ObjectMapper m = new ObjectMapper();
         JsonNode tree = m.readTree(SAMPLE_DOC_JSON_SPEC);
-        JsonParser jp = tree.traverse();
-        verifyJsonSpecSampleDoc(jp, true);
-        jp.close();
+        JsonParser p = tree.traverse();
+        verifyJsonSpecSampleDoc(p, true);
+        p.close();
     }
 
     public void testBinaryPojo() throws Exception
     {
         byte[] inputBinary = new byte[] { 1, 2, 100 };
         POJONode n = new POJONode(inputBinary);
-        JsonParser jp = n.traverse();
+        JsonParser p = n.traverse();
 
-        assertNull(jp.getCurrentToken());
-        assertToken(JsonToken.VALUE_EMBEDDED_OBJECT, jp.nextToken());
-        byte[] data = jp.getBinaryValue();
+        assertNull(p.getCurrentToken());
+        assertToken(JsonToken.VALUE_EMBEDDED_OBJECT, p.nextToken());
+        byte[] data = p.getBinaryValue();
         assertNotNull(data);
         assertArrayEquals(inputBinary, data);
-        Object pojo = jp.getEmbeddedObject();
+        Object pojo = p.getEmbeddedObject();
         assertSame(data, pojo);
-        jp.close();
+        p.close();
     }
 
     public void testBinaryNode() throws Exception
     {
         byte[] inputBinary = new byte[] { 0, -5 };
         BinaryNode n = new BinaryNode(inputBinary);
-        JsonParser jp = n.traverse();
+        JsonParser p = n.traverse();
 
-        assertNull(jp.getCurrentToken());
+        assertNull(p.getCurrentToken());
         // exposed as POJO... not as VALUE_STRING
-        assertToken(JsonToken.VALUE_EMBEDDED_OBJECT, jp.nextToken());
-        byte[] data = jp.getBinaryValue();
+        assertToken(JsonToken.VALUE_EMBEDDED_OBJECT, p.nextToken());
+        byte[] data = p.getBinaryValue();
         assertNotNull(data);
         assertArrayEquals(inputBinary, data);
 
         // but as importantly, can be viewed as base64 encoded thing:
-        assertEquals("APs=", jp.getText());
+        assertEquals("APs=", p.getText());
 
-        assertNull(jp.nextToken());
-        jp.close();
+        assertNull(p.nextToken());
+        p.close();
     }
 
     public void testTextAsBinary() throws Exception
     {
         TextNode n = new TextNode("   APs=\n");
-        JsonParser jp = n.traverse();
-        assertNull(jp.getCurrentToken());
-        assertToken(JsonToken.VALUE_STRING, jp.nextToken());
-        byte[] data = jp.getBinaryValue();
+        JsonParser p = n.traverse();
+        assertNull(p.getCurrentToken());
+        assertToken(JsonToken.VALUE_STRING, p.nextToken());
+        byte[] data = p.getBinaryValue();
         assertNotNull(data);
         assertArrayEquals(new byte[] { 0, -5 }, data);
 
-        assertNull(jp.nextToken());
-        jp.close();
-        assertTrue(jp.isClosed());
+        assertNull(p.nextToken());
+        p.close();
+        assertTrue(p.isClosed());
 
         // Also: let's verify we get an exception for garbage...
         n = new TextNode("?!??");
-        jp = n.traverse();
-        assertToken(JsonToken.VALUE_STRING, jp.nextToken());
+        p = n.traverse();
+        assertToken(JsonToken.VALUE_STRING, p.nextToken());
         try {
-            jp.getBinaryValue();
-        } catch (JsonParseException e) {
+            p.getBinaryValue();
+        } catch (InvalidFormatException e) {
             verifyException(e, "Illegal character");
         }
-        jp.close();
+        p.close();
     }
 
     /**
diff --git a/src/test/java/com/fasterxml/jackson/databind/node/TestTreeWithType.java b/src/test/java/com/fasterxml/jackson/databind/node/TestTreeWithType.java
index 959133e..8395627 100644
--- a/src/test/java/com/fasterxml/jackson/databind/node/TestTreeWithType.java
+++ b/src/test/java/com/fasterxml/jackson/databind/node/TestTreeWithType.java
@@ -20,7 +20,7 @@
         }
     }
 
-    // [Issue#353]
+    // [databind#353]
     public class SavedCookie {
         public String name, value;
 
diff --git a/src/test/java/com/fasterxml/jackson/databind/node/TextNodeTest.java b/src/test/java/com/fasterxml/jackson/databind/node/TextNodeTest.java
new file mode 100644
index 0000000..ad04c8a
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/node/TextNodeTest.java
@@ -0,0 +1,31 @@
+package com.fasterxml.jackson.databind.node;
+
+public class TextNodeTest extends NodeTestBase
+{
+    public void testText()
+    {
+        assertNull(TextNode.valueOf(null));
+        TextNode empty = TextNode.valueOf("");
+        assertStandardEquals(empty);
+        assertSame(TextNode.EMPTY_STRING_NODE, empty);
+
+        assertNodeNumbers(TextNode.valueOf("-3"), -3, -3.0);
+        assertNodeNumbers(TextNode.valueOf("17.75"), 17, 17.75);
+    
+        long value = 127353264013893L;
+        TextNode n = TextNode.valueOf(String.valueOf(value));
+        assertEquals(value, n.asLong());
+        
+        // and then with non-numeric input
+        n = TextNode.valueOf("foobar");
+        assertNodeNumbersForNonNumeric(n);
+
+        assertEquals("foobar", n.asText("barf"));
+        assertEquals("", empty.asText("xyz"));
+
+        assertTrue(TextNode.valueOf("true").asBoolean(true));
+        assertTrue(TextNode.valueOf("true").asBoolean(false));
+        assertFalse(TextNode.valueOf("false").asBoolean(true));
+        assertFalse(TextNode.valueOf("false").asBoolean(false));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/node/TreeReadViaMapperTest.java b/src/test/java/com/fasterxml/jackson/databind/node/TreeReadViaMapperTest.java
new file mode 100644
index 0000000..dbeed46
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/node/TreeReadViaMapperTest.java
@@ -0,0 +1,192 @@
+package com.fasterxml.jackson.databind.node;
+
+import java.io.*;
+import java.util.*;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.node.TestTreeDeserialization.Bean;
+
+/**
+ * This unit test suite tries to verify that ObjectMapper
+ * can properly parse JSON and bind contents into appropriate
+ * JsonNode instances.
+ */
+public class TreeReadViaMapperTest extends BaseMapTest
+{
+    public void testSimple() throws Exception
+    {
+        final String JSON = SAMPLE_DOC_JSON_SPEC;
+
+        for (int type = 0; type < 2; ++type) {
+            JsonNode result;
+
+            if (type == 0) {
+                result = objectMapper().readTree(new StringReader(JSON));
+            } else {
+                result = objectMapper().readTree(JSON);
+            }
+
+            assertType(result, ObjectNode.class);
+            assertEquals(1, result.size());
+            assertTrue(result.isObject());
+            
+            ObjectNode main = (ObjectNode) result;
+            assertEquals("Image", main.fieldNames().next());
+            JsonNode ob = main.elements().next();
+            assertType(ob, ObjectNode.class);
+            ObjectNode imageMap = (ObjectNode) ob;
+            
+            assertEquals(5, imageMap.size());
+            ob = imageMap.get("Width");
+            assertTrue(ob.isIntegralNumber());
+            assertFalse(ob.isFloatingPointNumber());
+            assertEquals(SAMPLE_SPEC_VALUE_WIDTH, ob.intValue());
+            ob = imageMap.get("Height");
+            assertTrue(ob.isIntegralNumber());
+            assertEquals(SAMPLE_SPEC_VALUE_HEIGHT, ob.intValue());
+            
+            ob = imageMap.get("Title");
+            assertTrue(ob.isTextual());
+            assertEquals(SAMPLE_SPEC_VALUE_TITLE, ob.textValue());
+            
+            ob = imageMap.get("Thumbnail");
+            assertType(ob, ObjectNode.class);
+            ObjectNode tn = (ObjectNode) ob;
+            ob = tn.get("Url");
+            assertTrue(ob.isTextual());
+            assertEquals(SAMPLE_SPEC_VALUE_TN_URL, ob.textValue());
+            ob = tn.get("Height");
+            assertTrue(ob.isIntegralNumber());
+            assertEquals(SAMPLE_SPEC_VALUE_TN_HEIGHT, ob.intValue());
+            ob = tn.get("Width");
+            assertTrue(ob.isTextual());
+            assertEquals(SAMPLE_SPEC_VALUE_TN_WIDTH, ob.textValue());
+            
+            ob = imageMap.get("IDs");
+            assertTrue(ob.isArray());
+            ArrayNode idList = (ArrayNode) ob;
+            assertEquals(4, idList.size());
+            assertEquals(4, calcLength(idList.elements()));
+            assertEquals(4, calcLength(idList.iterator()));
+            {
+                int[] values = new int[] {
+                    SAMPLE_SPEC_VALUE_TN_ID1,
+                    SAMPLE_SPEC_VALUE_TN_ID2,
+                    SAMPLE_SPEC_VALUE_TN_ID3,
+                    SAMPLE_SPEC_VALUE_TN_ID4
+                };
+                for (int i = 0; i < values.length; ++i) {
+                    assertEquals(values[i], idList.get(i).intValue());
+                }
+                int i = 0;
+                for (JsonNode n : idList) {
+                    assertEquals(values[i], n.intValue());
+                    ++i;
+                }
+            }
+        }
+    }
+
+    public void testMixed() throws IOException
+    {
+        ObjectMapper om = new ObjectMapper();
+        String JSON = "{\"node\" : { \"a\" : 3 }, \"x\" : 9 }";
+        Bean bean = om.readValue(JSON, Bean.class);
+
+        assertEquals(9, bean._x);
+        JsonNode n = bean._node;
+        assertNotNull(n);
+        assertEquals(1, n.size());
+        ObjectNode on = (ObjectNode) n;
+        assertEquals(3, on.get("a").intValue());
+    }
+
+    /**
+     * Type mappers should be able to gracefully deal with end of
+     * input.
+     */
+    public void testEOF() throws Exception
+    {
+        String JSON =
+            "{ \"key\": [ { \"a\" : { \"name\": \"foo\",  \"type\": 1\n"
+            +"},  \"type\": 3, \"url\": \"http://www.google.com\" } ],\n"
+            +"\"name\": \"xyz\", \"type\": 1, \"url\" : null }\n  "
+            ;
+        JsonFactory jf = new JsonFactory();
+        JsonParser p = jf.createParser(new StringReader(JSON));
+        JsonNode result = objectMapper().readTree(p);
+
+        assertTrue(result.isObject());
+        assertEquals(4, result.size());
+
+        assertNull(objectMapper().readTree(p));
+        p.close();
+    }
+
+    public void testMultiple() throws Exception
+    {
+        String JSON = "12  \"string\" [ 1, 2, 3 ]";
+        JsonFactory jf = new JsonFactory();
+        JsonParser p = jf.createParser(new StringReader(JSON));
+        final ObjectMapper mapper = objectMapper();
+        JsonNode result = mapper.readTree(p);
+
+        assertTrue(result.isIntegralNumber());
+        assertTrue(result.isInt());
+        assertFalse(result.isTextual());
+        assertEquals(12, result.intValue());
+
+        result = mapper.readTree(p);
+        assertTrue(result.isTextual());
+        assertFalse(result.isIntegralNumber());
+        assertFalse(result.isInt());
+        assertEquals("string", result.textValue());
+
+        result = mapper.readTree(p);
+        assertTrue(result.isArray());
+        assertEquals(3, result.size());
+
+        assertNull(mapper.readTree(p));
+        p.close();
+    }
+
+    // [databind#1406]
+    public void testNullFromEOFViaMapper() throws Exception
+    {
+        final ObjectMapper mapper = objectMapper();
+
+        assertNull(mapper.readTree(new StringReader("")));
+        assertNull(mapper.readTree(new ByteArrayInputStream(new byte[0])));
+    }
+
+    // [databind#1406]
+    public void testNullFromEOFViaObjectReader() throws Exception
+    {
+        final ObjectMapper mapper = objectMapper();
+
+        assertNull(mapper.readTree(new StringReader("")));
+        assertNull(mapper.readTree(new ByteArrayInputStream(new byte[0])));
+        assertNull(mapper.readerFor(JsonNode.class)
+                .readTree(new StringReader("")));
+        assertNull(mapper.readerFor(JsonNode.class)
+                .readTree(new ByteArrayInputStream(new byte[0])));
+    }
+
+    /*
+    /**********************************************
+    /* Helper methods
+    /**********************************************
+     */
+
+    private int calcLength(Iterator<JsonNode> it)
+    {
+        int count = 0;
+        while (it.hasNext()) {
+            it.next();
+            ++count;
+        }
+        return count;
+    }
+}
+
diff --git a/src/test/java/com/fasterxml/jackson/databind/objectid/JSOGDeserialize622Test.java b/src/test/java/com/fasterxml/jackson/databind/objectid/JSOGDeserialize622Test.java
index 129872e..1adfd10 100644
--- a/src/test/java/com/fasterxml/jackson/databind/objectid/JSOGDeserialize622Test.java
+++ b/src/test/java/com/fasterxml/jackson/databind/objectid/JSOGDeserialize622Test.java
@@ -97,14 +97,14 @@
     static class JSOGRefDeserializer extends JsonDeserializer<JSOGRef>
     {
       @Override
-      public JSOGRef deserialize(JsonParser jp, DeserializationContext ctx) throws IOException {
-          JsonNode node = jp.readValueAsTree();
+      public JSOGRef deserialize(JsonParser p, DeserializationContext ctx) throws IOException {
+          JsonNode node = p.readValueAsTree();
           if (node.isTextual()) {
               return new JSOGRef(node.asInt());
           }
           JsonNode n = node.get(REF_KEY);
           if (n == null) {
-              throw JsonMappingException.from(jp, "Could not find key '"+REF_KEY
+              throw new JsonMappingException(p, "Could not find key '"+REF_KEY
                       +"' from ("+node.getClass().getName()+"): "+node);
           }
           return new JSOGRef(n.asInt());
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestObjectId687.java b/src/test/java/com/fasterxml/jackson/databind/objectid/ObjectId687Test.java
similarity index 76%
rename from src/test/java/com/fasterxml/jackson/failing/TestObjectId687.java
rename to src/test/java/com/fasterxml/jackson/databind/objectid/ObjectId687Test.java
index fd8fabc..972a5fa 100644
--- a/src/test/java/com/fasterxml/jackson/failing/TestObjectId687.java
+++ b/src/test/java/com/fasterxml/jackson/databind/objectid/ObjectId687Test.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.failing;
+package com.fasterxml.jackson.databind.objectid;
 
 import java.io.IOException;
 import java.util.*;
@@ -6,8 +6,9 @@
 import com.fasterxml.jackson.annotation.*;
 import com.fasterxml.jackson.databind.*;
 
-public class TestObjectId687 extends BaseMapTest
+public class ObjectId687Test extends BaseMapTest
 {
+    // for [databind#687]
     @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="label")
     static class ReferredWithCreator {
         public String label;
@@ -63,7 +64,8 @@
      */
 
     private final ObjectMapper MAPPER = objectMapper();
-    
+
+    // for [databind#687]
     public void testSerializeDeserializeWithCreator() throws IOException {
         ReferredWithCreator base = new ReferredWithCreator("label1");
         ReferringToObjWithCreator r = new ReferringToObjWithCreator();
@@ -72,10 +74,15 @@
         e.baseRef = base;
         e.nextRef = r;
 
-        String jsonStr = MAPPER.writeValueAsString(e);
+        String json = MAPPER.writeValueAsString(e);
 
-        EnclosingForRefsWithCreator deserialized = MAPPER.readValue(jsonStr, EnclosingForRefsWithCreator.class);
-        assertNotNull(deserialized);
+        EnclosingForRefsWithCreator result = MAPPER.readValue(json,
+                EnclosingForRefsWithCreator.class);
+        assertNotNull(result);
+        assertEquals(result.label, e.label);
+
+        // also, compare by re-serializing:
+        assertEquals(json, MAPPER.writeValueAsString(result));
     }
 
     public void testSerializeDeserializeNoCreator() throws IOException {
@@ -86,9 +93,14 @@
         e.baseRef = base;
         e.nextRef = r;
 
-        String jsonStr = MAPPER.writeValueAsString(e);
+        String json = MAPPER.writeValueAsString(e);
 
-        EnclosingForRefWithNoCreator deserialized = MAPPER.readValue(jsonStr, EnclosingForRefWithNoCreator.class);
-        assertNotNull(deserialized);
+        EnclosingForRefWithNoCreator result = MAPPER.readValue(json,
+                EnclosingForRefWithNoCreator.class);
+        assertNotNull(result);
+        assertEquals(result.label, e.label);
+
+        // also, compare by re-serializing:
+        assertEquals(json, MAPPER.writeValueAsString(result));
     }    
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/objectid/PolymorphicWithObjectId1551Test.java b/src/test/java/com/fasterxml/jackson/databind/objectid/PolymorphicWithObjectId1551Test.java
new file mode 100644
index 0000000..a244ca4
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/objectid/PolymorphicWithObjectId1551Test.java
@@ -0,0 +1,91 @@
+package com.fasterxml.jackson.databind.objectid;
+
+import com.fasterxml.jackson.annotation.JsonIdentityInfo;
+import com.fasterxml.jackson.annotation.JsonIdentityReference;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.annotation.ObjectIdGenerators;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
+
+public class PolymorphicWithObjectId1551Test extends BaseMapTest
+{
+    @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY,
+            property = "@class")
+    static abstract class Vehicle {
+        public String vehicleId;
+    }
+
+    static class Car extends Vehicle {
+        public int numberOfDoors;
+    }
+
+    static class VehicleOwnerViaProp {
+        @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "vehicleId")
+        @JsonIdentityReference(alwaysAsId = false)
+        public Vehicle ownedVehicle;
+    }
+
+    static class VehicleOwnerBroken {
+        @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "bogus")
+        @JsonIdentityReference(alwaysAsId = false)
+        public Vehicle ownedVehicle;
+    }
+
+    public void testWithAbstractUsingProp() throws Exception
+    {
+        Car c = new Car();
+        c.vehicleId = "123";
+        c.numberOfDoors = 2;
+        // both owners own the same vehicle (car sharing ;-))
+        VehicleOwnerViaProp v1 = new VehicleOwnerViaProp();
+        v1.ownedVehicle = c;
+        VehicleOwnerViaProp v2 = new VehicleOwnerViaProp();
+        v2.ownedVehicle = c;
+
+        ObjectMapper objectMapper = new ObjectMapper();
+        String serialized = objectMapper.writer()
+                .writeValueAsString(new VehicleOwnerViaProp[] { v1, v2 });
+        // 02-May-2017, tatu: Not possible to support as of Jackson 2.8 at least, so:
+
+        VehicleOwnerViaProp[] deserialized = objectMapper.readValue(serialized, VehicleOwnerViaProp[].class);
+        assertEquals(2, deserialized.length);
+        assertSame(deserialized[0].ownedVehicle, deserialized[1].ownedVehicle);
+    }
+
+    public void testFailingAbstractUsingProp() throws Exception
+    {
+        Car c = new Car();
+        c.vehicleId = "123";
+        c.numberOfDoors = 2;
+        // both owners own the same vehicle (car sharing ;-))
+        VehicleOwnerBroken v1 = new VehicleOwnerBroken();
+        v1.ownedVehicle = c;
+        VehicleOwnerBroken v2 = new VehicleOwnerBroken();
+        v2.ownedVehicle = c;
+
+        ObjectMapper objectMapper = new ObjectMapper();
+        try {
+            objectMapper.writer()
+                .writeValueAsString(new VehicleOwnerBroken[] { v1, v2 });
+        } catch (InvalidDefinitionException e) {
+            // on serialization, reported for different type
+            assertEquals(Car.class, e.getType().getRawClass());
+            verifyException(e, "Invalid Object Id definition");
+            verifyException(e, "cannot find property with name 'bogus'");
+        }
+
+        // and same for deser
+        final String JSON = aposToQuotes(
+"[{'ownedVehicle':{'@class':'com.fasterxml.jackson.failing.PolymorphicWithObjectId1551Test$Car','vehicleId':'123',"
++"'numberOfDoors':2}},{'ownedVehicle':'123'}]"
+                );
+        try {
+            objectMapper.readValue(JSON, VehicleOwnerBroken[].class);
+            fail("Should not pass");
+        } catch (InvalidDefinitionException e) {
+            assertEquals(Vehicle.class, e.getType().getRawClass());
+            verifyException(e, "Invalid Object Id definition");
+            verifyException(e, "cannot find property with name 'bogus'");
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdSerialization.java b/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdSerialization.java
index 6d98d90..537d09f 100644
--- a/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdSerialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdSerialization.java
@@ -331,7 +331,7 @@
             MAPPER.writeValueAsString(new Broken());
             fail("Should have thrown an exception");
         } catch (JsonMappingException e) {
-            verifyException(e, "can not find property with name 'id'");
+            verifyException(e, "cannot find property with name 'id'");
         }
     }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdWithPolymorphic.java b/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdWithPolymorphic.java
index 2ecca8e..486abf3 100644
--- a/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdWithPolymorphic.java
+++ b/src/test/java/com/fasterxml/jackson/databind/objectid/TestObjectIdWithPolymorphic.java
@@ -2,11 +2,9 @@
 
 import java.util.*;
 
-import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
 import com.fasterxml.jackson.annotation.JsonIdentityInfo;
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
 import com.fasterxml.jackson.annotation.ObjectIdGenerators;
-import com.fasterxml.jackson.annotation.PropertyAccessor;
 
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
@@ -134,11 +132,6 @@
     public void testIssue811() throws Exception
     {
         ObjectMapper om = new ObjectMapper();
-        om.disable(MapperFeature.AUTO_DETECT_CREATORS);
-        om.disable(MapperFeature.AUTO_DETECT_GETTERS);
-        om.disable(MapperFeature.AUTO_DETECT_IS_GETTERS);
-        om.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
-        
         om.enable(SerializationFeature.WRITE_ENUMS_USING_INDEX);
         om.enable(SerializationFeature.INDENT_OUTPUT);
         om.enableDefaultTypingAsProperty(DefaultTyping.NON_FINAL, "@class");
diff --git a/src/test/java/com/fasterxml/jackson/databind/seq/ObjectReaderTest.java b/src/test/java/com/fasterxml/jackson/databind/seq/ObjectReaderTest.java
deleted file mode 100644
index f64d50b..0000000
--- a/src/test/java/com/fasterxml/jackson/databind/seq/ObjectReaderTest.java
+++ /dev/null
@@ -1,202 +0,0 @@
-package com.fasterxml.jackson.databind.seq;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-import com.fasterxml.jackson.core.FormatSchema;
-import com.fasterxml.jackson.core.JsonFactory;
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonPointer;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.*;
-import com.fasterxml.jackson.databind.cfg.ContextAttributes;
-import com.fasterxml.jackson.databind.node.JsonNodeFactory;
-
-public class ObjectReaderTest extends BaseMapTest
-{
-    final ObjectMapper MAPPER = new ObjectMapper();
-
-    static class POJO {
-        public Map<String, Object> name;
-    }
-
-    public void testSimpleViaParser() throws Exception
-    {
-        final String JSON = "[1]";
-        JsonParser p = MAPPER.getFactory().createParser(JSON);
-        Object ob = MAPPER.readerFor(Object.class)
-                .readValue(p);
-        p.close();
-        assertTrue(ob instanceof List<?>);
-    }
-
-    public void testParserFeatures() throws Exception
-    {
-        final String JSON = "[ /* foo */ 7 ]";
-        // default won't accept comments, let's change that:
-        ObjectReader reader = MAPPER.readerFor(int[].class)
-                .with(JsonParser.Feature.ALLOW_COMMENTS);
-
-        int[] value = reader.readValue(JSON);
-        assertNotNull(value);
-        assertEquals(1, value.length);
-        assertEquals(7, value[0]);
-
-        // but also can go back
-        try {
-            reader.without(JsonParser.Feature.ALLOW_COMMENTS).readValue(JSON);
-            fail("Should not have passed");
-        } catch (JsonProcessingException e) {
-            verifyException(e, "foo");
-        }
-    }
-    
-    public void testNoPointerLoading() throws Exception {
-        final String source = "{\"foo\":{\"bar\":{\"caller\":{\"name\":{\"value\":1234}}}}}";
-
-        JsonNode tree = MAPPER.readTree(source);
-        JsonNode node = tree.at("/foo/bar/caller");
-        POJO pojo = MAPPER.treeToValue(node, POJO.class);
-        assertTrue(pojo.name.containsKey("value"));
-        assertEquals(1234, pojo.name.get("value"));
-    }
-
-    public void testPointerLoading() throws Exception {
-        final String source = "{\"foo\":{\"bar\":{\"caller\":{\"name\":{\"value\":1234}}}}}";
-
-        ObjectReader reader = MAPPER.readerFor(POJO.class).at("/foo/bar/caller");
-
-        POJO pojo = reader.readValue(source);
-        assertTrue(pojo.name.containsKey("value"));
-        assertEquals(1234, pojo.name.get("value"));
-    }
-
-    public void testPointerLoadingAsJsonNode() throws Exception {
-        final String source = "{\"foo\":{\"bar\":{\"caller\":{\"name\":{\"value\":1234}}}}}";
-
-        ObjectReader reader = MAPPER.readerFor(POJO.class).at(JsonPointer.compile("/foo/bar/caller"));
-
-        JsonNode node = reader.readTree(source);
-        assertTrue(node.has("name"));
-        assertEquals("{\"value\":1234}", node.get("name").toString());
-    }
-
-    public void testPointerLoadingMappingIteratorOne() throws Exception {
-        final String source = "{\"foo\":{\"bar\":{\"caller\":{\"name\":{\"value\":1234}}}}}";
-
-        ObjectReader reader = MAPPER.readerFor(POJO.class).at("/foo/bar/caller");
-
-        MappingIterator<POJO> itr = reader.readValues(source);
-
-        POJO pojo = itr.next();
-
-        assertTrue(pojo.name.containsKey("value"));
-        assertEquals(1234, pojo.name.get("value"));
-        assertFalse(itr.hasNext());
-        itr.close();
-    }
-    
-    public void testPointerLoadingMappingIteratorMany() throws Exception {
-        final String source = "{\"foo\":{\"bar\":{\"caller\":[{\"name\":{\"value\":1234}}, {\"name\":{\"value\":5678}}]}}}";
-
-        ObjectReader reader = MAPPER.readerFor(POJO.class).at("/foo/bar/caller");
-
-        MappingIterator<POJO> itr = reader.readValues(source);
-
-        POJO pojo = itr.next();
-
-        assertTrue(pojo.name.containsKey("value"));
-        assertEquals(1234, pojo.name.get("value"));
-        assertTrue(itr.hasNext());
-        
-        pojo = itr.next();
-
-        assertNotNull(pojo.name);
-        assertTrue(pojo.name.containsKey("value"));
-        assertEquals(5678, pojo.name.get("value"));
-        assertFalse(itr.hasNext());
-        itr.close();
-    }
-
-    public void testNodeHandling() throws Exception
-    {
-        JsonNodeFactory nodes = new JsonNodeFactory(true);
-        ObjectReader r = MAPPER.reader().with(nodes);
-        assertTrue(r.createArrayNode().isArray());
-        assertTrue(r.createObjectNode().isObject());
-    }
-
-    public void testSettings() throws Exception
-    {
-        ObjectReader r = MAPPER.reader();
-        assertSame(MAPPER.getFactory(), r.getFactory());
-
-        JsonFactory f = new JsonFactory();
-        r = r.with(f);
-        assertSame(f, r.getFactory());
-
-        assertNotNull(r.getTypeFactory());
-        assertNull(r.getInjectableValues());
-
-        r = r.withAttributes(Collections.emptyMap());
-        ContextAttributes attrs = r.getAttributes();
-        assertNotNull(attrs);
-        assertNull(attrs.getAttribute("abc"));
-
-        r = r.forType(MAPPER.constructType(String.class));
-        r = r.withRootName(PropertyName.construct("foo"));
-
-        r = r.withoutFeatures(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES,
-                DeserializationFeature.FAIL_ON_INVALID_SUBTYPE);
-        assertFalse(r.isEnabled(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES));
-        assertFalse(r.isEnabled(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE));
-        r = r.withFeatures(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES,
-                DeserializationFeature.FAIL_ON_INVALID_SUBTYPE);
-        assertTrue(r.isEnabled(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES));
-        assertTrue(r.isEnabled(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE));
-    }
-
-    public void testNoPrefetch() throws Exception
-    {
-        ObjectReader r = MAPPER.reader()
-                .without(DeserializationFeature.EAGER_DESERIALIZER_FETCH);
-        Number n = r.forType(Integer.class).readValue("123 ");
-        assertEquals(Integer.valueOf(123), n);
-    }
-
-    /*
-    /**********************************************************
-    /* Test methods, failures
-    /**********************************************************
-     */
-
-    public void testMissingType() throws Exception
-    {
-        ObjectReader r = MAPPER.reader();
-        try {
-            r.readValue("1");
-            fail("Should not pass");
-        } catch (JsonMappingException e) {
-            verifyException(e, "No value type configured");
-        }
-    }
-
-    public void testSchema() throws Exception
-    {
-        ObjectReader r = MAPPER.readerFor(String.class);
-        
-        // Ok to try to set `null` schema, always:
-        r = r.with((FormatSchema) null);
-
-        try {
-            // but not schema that doesn't match format (no schema exists for json)
-            r = r.with(new BogusSchema())
-                .readValue(quote("foo"));
-            
-            fail("Should not pass");
-        } catch (IllegalArgumentException e) {
-            verifyException(e, "Can not use FormatSchema");
-        }
-    }
-}
diff --git a/src/test/java/com/fasterxml/jackson/databind/seq/SequenceWriterTest.java b/src/test/java/com/fasterxml/jackson/databind/seq/SequenceWriterTest.java
index 676383f..c103a34 100644
--- a/src/test/java/com/fasterxml/jackson/databind/seq/SequenceWriterTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/seq/SequenceWriterTest.java
@@ -1,12 +1,17 @@
 package com.fasterxml.jackson.databind.seq;
 
+import java.io.Closeable;
+import java.io.IOException;
 import java.io.StringWriter;
 import java.util.*;
 
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
 import com.fasterxml.jackson.annotation.JsonTypeName;
+
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.core.io.SerializedString;
+
 import com.fasterxml.jackson.databind.*;
 
 public class SequenceWriterTest extends BaseMapTest
@@ -43,6 +48,40 @@
         public ImplB(int v) { b = v; }
     }
 
+    static class BareBase {
+        public int a = 1;
+    }
+
+    @JsonPropertyOrder({ "a", "b" })
+    static class BareBaseExt extends BareBase {
+        public int b = 2;
+    }
+
+    static class BareBaseCloseable extends BareBase
+        implements Closeable
+    {
+        public int c = 3;
+
+        boolean closed = false;
+        
+        @Override
+        public void close() throws IOException {
+            closed = true;
+        }
+    }
+
+    static class CloseableValue implements Closeable
+    {
+        public int x;
+
+        public boolean closed;
+        
+        @Override
+        public void close() throws IOException {
+            closed = true;
+        }
+    }
+
     /*
     /**********************************************************
     /* Test methods, simple writes
@@ -57,6 +96,7 @@
     {
         StringWriter strw = new StringWriter();
         SequenceWriter w = WRITER
+                .forType(Bean.class)
                 .writeValues(strw);
         w.write(new Bean(13))
             .write(new Bean(-6))
@@ -136,7 +176,6 @@
                 strw.toString());
     }
 
-    @SuppressWarnings("resource")
     public void testPolymorphicArrayWithType() throws Exception
     {
         StringWriter strw = new StringWriter();
@@ -145,9 +184,53 @@
                 .writeValuesAsArray(strw);
         w.write(new ImplA(-1))
             .write(new ImplB(3))
-            .write(new ImplA(7))
-            .close();
+            .write(new ImplA(7));
+        w.flush();
+        w.close();
         assertEquals(aposToQuotes("[{'type':'A','value':-1},{'type':'B','b':3},{'type':'A','value':7}]"),
                 strw.toString());
     }
+
+    @SuppressWarnings("resource")
+    public void testSimpleCloseable() throws Exception
+    {
+        ObjectWriter w = MAPPER.writer()
+                .with(SerializationFeature.CLOSE_CLOSEABLE);
+        CloseableValue input = new CloseableValue();
+        assertFalse(input.closed);
+        StringWriter out = new StringWriter();
+        SequenceWriter seq = w.writeValues(out);
+        input = new CloseableValue();
+        assertFalse(input.closed);
+        seq.write(input);
+        assertTrue(input.closed);
+        seq.close();
+        input.close();
+        assertEquals(aposToQuotes("{'x':0,'closed':false}"), out.toString());
+    }
+
+    public void testWithExplicitType() throws Exception
+    {
+        ObjectWriter w = MAPPER.writer()
+                // just for fun (and higher coverage):
+                .without(SerializationFeature.FLUSH_AFTER_WRITE_VALUE)
+                .with(SerializationFeature.CLOSE_CLOSEABLE);
+        StringWriter out = new StringWriter();
+        SequenceWriter seq = w.writeValues(out);
+        // first full, as-is
+        seq.write(new BareBaseExt());
+        // but then just base type (no 'b' field)
+        seq.write(new BareBaseExt(), MAPPER.constructType(BareBase.class));
+
+        // one more. And note! Check for Closeable is for _value_, not type
+        // so it's fine to expect closing here
+        BareBaseCloseable cl = new BareBaseCloseable();
+        seq.write(cl, MAPPER.constructType(BareBase.class));
+        assertTrue(cl.closed);
+        cl.close();
+
+        seq.close();
+        seq.flush();
+        assertEquals(aposToQuotes("{'a':1,'b':2} {'a':1} {'a':1}"), out.toString());
+    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestInnerClassReaderFor.java b/src/test/java/com/fasterxml/jackson/databind/seq/TestInnerClassReaderFor.java
similarity index 95%
rename from src/test/java/com/fasterxml/jackson/failing/TestInnerClassReaderFor.java
rename to src/test/java/com/fasterxml/jackson/databind/seq/TestInnerClassReaderFor.java
index 7fef656..6cf85e4 100644
--- a/src/test/java/com/fasterxml/jackson/failing/TestInnerClassReaderFor.java
+++ b/src/test/java/com/fasterxml/jackson/databind/seq/TestInnerClassReaderFor.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.failing;
+package com.fasterxml.jackson.databind.seq;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.databind.BaseMapTest;
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/AnyGetterTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/AnyGetterTest.java
index 871f7b8..23746d4 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/AnyGetterTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/AnyGetterTest.java
@@ -35,6 +35,18 @@
         }
     }
 
+    // For [databind#1376]: allow disabling any-getter
+    static class NotEvenAnyBean extends AnyOnlyBean
+    {
+        @JsonAnyGetter(enabled=false)
+        @Override
+        public Map<String,Integer> any() {
+            throw new RuntimeException("Should not get called!)");
+        }
+
+        public int getValue() { return 42; }
+    }
+    
     static class MapAsAny
     {
         protected Map<String,Object> stuff = new LinkedHashMap<String,Object>();
@@ -121,13 +133,13 @@
 
     /*
     /**********************************************************
-    /* Test cases
+    /* Test methods
     /**********************************************************
      */
 
     private final ObjectMapper MAPPER = new ObjectMapper();
     
-    public void testSimpleJsonValue() throws Exception
+    public void testSimpleAnyBean() throws Exception
     {
         String json = MAPPER.writeValueAsString(new Bean());
         Map<?,?> map = MAPPER.readValue(json, Map.class);
@@ -153,6 +165,12 @@
         assertEquals("{\"a\":3}", json);
     }
 
+    public void testAnyDisabling() throws Exception
+    {
+        String json = MAPPER.writeValueAsString(new NotEvenAnyBean());
+        assertEquals(aposToQuotes("{'value':42}"), json);
+    }
+
     // Trying to repro [databind#577]
     public void testAnyWithNull() throws Exception
     {
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/BeanSerializerModifier1612Test.java b/src/test/java/com/fasterxml/jackson/databind/ser/BeanSerializerModifier1612Test.java
new file mode 100644
index 0000000..2be2594
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/BeanSerializerModifier1612Test.java
@@ -0,0 +1,66 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
+import com.fasterxml.jackson.databind.ser.BeanSerializerBuilder;
+import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
+
+public class BeanSerializerModifier1612Test extends BaseMapTest
+{
+    @JsonPropertyOrder({ "a", "b", "c" })
+    static class Bean1612 {
+        public Integer a;
+        public Integer b;
+        public Double c;
+
+        public Bean1612(Integer a, Integer b, Double c) {
+            this.a = a;
+            this.b = b;
+            this.c = c;
+        }
+    }
+
+    static class Modifier1612 extends BeanSerializerModifier {
+        @Override
+        public BeanSerializerBuilder updateBuilder(SerializationConfig config, BeanDescription beanDesc,
+                BeanSerializerBuilder builder) {
+            List<BeanPropertyWriter> filtered = new ArrayList<BeanPropertyWriter>(2);
+            List<BeanPropertyWriter> properties = builder.getProperties();
+            //Make the filtered properties list bigger
+            builder.setFilteredProperties(new BeanPropertyWriter[] {properties.get(0), properties.get(1), properties.get(2)});
+
+            //The props will be shorter
+            filtered.add(properties.get(1));
+            filtered.add(properties.get(2));
+            builder.setProperties(filtered);
+            return builder;
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Construction and setter methods
+    /**********************************************************
+     */
+
+    public void testIssue1612() throws Exception
+    {
+        SimpleModule mod = new SimpleModule();
+        mod.setSerializerModifier(new Modifier1612());
+        ObjectMapper objectMapper = new ObjectMapper()
+                .registerModule(mod);
+        try {
+            objectMapper.writeValueAsString(new Bean1612(0, 1, 2d));
+            fail("Should not pass");
+        } catch (InvalidDefinitionException e) {
+            verifyException(e, "Failed to construct BeanSerializer");
+            verifyException(e, Bean1612.class.getName());
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestBeanSerializer.java b/src/test/java/com/fasterxml/jackson/databind/ser/BeanSerializerModifierTest.java
similarity index 97%
rename from src/test/java/com/fasterxml/jackson/databind/ser/TestBeanSerializer.java
rename to src/test/java/com/fasterxml/jackson/databind/ser/BeanSerializerModifierTest.java
index 915a2ab..6f0e571 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/TestBeanSerializer.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/BeanSerializerModifierTest.java
@@ -23,7 +23,7 @@
  * construction of {@link BeanSerializer} instances.
  */
 @SuppressWarnings("serial")
-public class TestBeanSerializer extends BaseMapTest
+public class BeanSerializerModifierTest extends BaseMapTest
 {
     static class SerializerModifierModule extends SimpleModule
     {
@@ -142,8 +142,6 @@
         }
     }
 
-    // for [JACKSON-670]
-    
     static class EmptyBean {
         @JsonIgnore
         public String name = "foo";
@@ -163,7 +161,8 @@
                 beanProperties.add(new BeanPropertyWriter(prop, f, null,
                         strType,
                         null, null, strType,
-                        false, null));
+                        false, null,
+                        null));
             } catch (NoSuchFieldException e) {
                 throw new IllegalStateException(e.getMessage());
             }
@@ -187,7 +186,7 @@
             return new BogusBeanSerializer(42);
         }
     }
-    // [Issue#120], arrays, collections, maps
+    // [databind#120], arrays, collections, maps
     
     static class ArraySerializerModifier extends BeanSerializerModifier {
         @Override
@@ -248,9 +247,7 @@
             };
         }
     }
-    
-    enum EnumABC { A, B, C };
-    
+
     /*
     /********************************************************
     /* Unit tests: success
@@ -304,7 +301,6 @@
         assertEquals("{\"bogus\":\"foo\"}", json);
     }
 
-    // [Issue#539]
     public void testEmptyBean539() throws Exception
     {
         ObjectMapper mapper = new ObjectMapper();
@@ -320,7 +316,7 @@
         assertEquals("42", json);
     }
     
-    // [Issue#121]
+    // [databind#121]
 
     public void testModifyArraySerializer() throws Exception
     {
@@ -351,7 +347,7 @@
         ObjectMapper mapper = new ObjectMapper();
         mapper.registerModule(new SimpleModule("test")
             .setSerializerModifier(new EnumSerializerModifier()));
-        assertEquals("123", mapper.writeValueAsString(EnumABC.C));
+        assertEquals("123", mapper.writeValueAsString(ABC.C));
     }
 
     public void testModifyKeySerializer() throws Exception
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonValue.java b/src/test/java/com/fasterxml/jackson/databind/ser/JsonValueTest.java
similarity index 81%
rename from src/test/java/com/fasterxml/jackson/databind/ser/TestJsonValue.java
rename to src/test/java/com/fasterxml/jackson/databind/ser/JsonValueTest.java
index 4b5b4d6..ec1adb5 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonValue.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/JsonValueTest.java
@@ -16,7 +16,7 @@
  * annotation with bean serialization.
  */
 @SuppressWarnings("serial")
-public class TestJsonValue
+public class JsonValueTest
     extends BaseMapTest
 {
     static class ValueClass<T>
@@ -26,12 +26,16 @@
         public ValueClass(T v) { _value = v; }
 
         @JsonValue T value() { return _value; }
-
-        // shouldn't need this, but may be useful for troubleshooting:
-        @Override
-        public String toString() { return "???"; }
     }
 
+    static class FieldValueClass<T>
+    {
+        @JsonValue(true)
+        final T _value;
+
+        public FieldValueClass(T v) { _value = v; }
+    }
+    
     /**
      * Another test class to check that it is also possible to
      * force specific serializer to use with @JsonValue annotated
@@ -54,8 +58,7 @@
     {
         public ToStringValueClass2(String value) { super(value); }
 
-        /* Simple as well, but let's ensure that other getters won't matter...
-         */
+        // Simple as well, but let's ensure that other getters won't matter...
 
         @JsonProperty int getFoobar() { return 4; }
 
@@ -87,6 +90,15 @@
         }
     }
 
+    static class MapFieldBean
+    {
+        @JsonValue
+        Map<String,String> stuff = new HashMap<>();
+        {
+            stuff.put("b", "2");
+        }
+    }
+    
     static class MapAsNumber extends HashMap<String,String>
     {
         @JsonValue
@@ -99,6 +111,17 @@
         public int value() { return 13; }
     }
 
+    // Just to ensure it's possible to disable annotation (usually
+    // via mix-ins, but here directly)
+    @JsonPropertyOrder({ "x", "y" })
+    static class DisabledJsonValue {
+        @JsonValue(false)
+        public int x = 1;
+
+        @JsonValue(false)
+        public int getY() { return 2; }
+    }
+
     static class IntExtBean {
         public List<Internal> values = new ArrayList<Internal>();
         
@@ -178,10 +201,16 @@
 
     private final ObjectMapper MAPPER = new ObjectMapper();
     
-    public void testSimpleJsonValue() throws Exception
+    public void testSimpleMethodJsonValue() throws Exception
     {
-        String result = MAPPER.writeValueAsString(new ValueClass<String>("abc"));
-        assertEquals("\"abc\"", result);
+        assertEquals("\"abc\"", MAPPER.writeValueAsString(new ValueClass<String>("abc")));
+        assertEquals("null", MAPPER.writeValueAsString(new ValueClass<String>(null)));
+    }
+
+    public void testSimpleFieldJsonValue() throws Exception
+    {
+        assertEquals("\"abc\"", MAPPER.writeValueAsString(new FieldValueClass<String>("abc")));
+        assertEquals("null", MAPPER.writeValueAsString(new FieldValueClass<String>(null)));
     }
 
     public void testJsonValueWithUseSerializer() throws Exception
@@ -199,6 +228,12 @@
         assertEquals("\"xyz\"", result);
     }
 
+    public void testDisabling() throws Exception
+    {
+        assertEquals(aposToQuotes("{'x':1,'y':2}"),
+                MAPPER.writeValueAsString(new DisabledJsonValue()));
+    }
+
     public void testValueWithStaticType() throws Exception
     {
         // Ok; first, with dynamic type:
@@ -211,12 +246,15 @@
     }
 
     public void testMapWithJsonValue() throws Exception {
+        // First via method
         assertEquals("{\"a\":\"1\"}", MAPPER.writeValueAsString(new MapBean()));
+
+        // then field
+        assertEquals("{\"b\":\"2\"}", MAPPER.writeValueAsString(new MapFieldBean()));
     }
 
     public void testWithMap() throws Exception {
         assertEquals("42", MAPPER.writeValueAsString(new MapAsNumber()));
-
     }
 
     public void testWithList() throws Exception {
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/NumberSerTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/NumberSerTest.java
deleted file mode 100644
index 7d573d6..0000000
--- a/src/test/java/com/fasterxml/jackson/databind/ser/NumberSerTest.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package com.fasterxml.jackson.databind.ser;
-
-import java.math.BigInteger;
-
-import com.fasterxml.jackson.annotation.JsonFormat;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.databind.BaseMapTest;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-/**
- * Unit tests for verifying serialization of simple basic non-structured
- * types; primitives (and/or their wrappers), Strings.
- */
-public class NumberSerTest extends BaseMapTest
-{
-    private final ObjectMapper MAPPER = objectMapper();
-
-    static class IntWrapper {
-        public int i;
-
-        public IntWrapper(int value) { i = value; }
-    }
-    
-    static class IntAsString {
-        @JsonFormat(shape=JsonFormat.Shape.STRING)
-        @JsonProperty("value")
-        public int foo = 3;
-    }
-
-    static class LongAsString {
-        @JsonFormat(shape=JsonFormat.Shape.STRING)
-        public long value = 4;
-    }
-
-    static class DoubleAsString {
-        @JsonFormat(shape=JsonFormat.Shape.STRING)
-        public double value = -0.5;
-    }
-
-    /*
-    /**********************************************************
-    /* Test methods
-    /**********************************************************
-     */
-
-    public void testDouble() throws Exception
-    {
-        double[] values = new double[] {
-            0.0, 1.0, 0.1, -37.01, 999.99, 0.3, 33.3, Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY
-        };
-        for (double d : values) {
-            String expected = String.valueOf(d);
-            if (Double.isNaN(d) || Double.isInfinite(d)) {
-                expected = "\""+d+"\"";
-            }
-            assertEquals(expected, MAPPER.writeValueAsString(Double.valueOf(d)));
-        }
-    }
-
-    public void testBigInteger() throws Exception
-    {
-        BigInteger[] values = new BigInteger[] {
-                BigInteger.ONE, BigInteger.TEN, BigInteger.ZERO,
-                BigInteger.valueOf(1234567890L),
-                new BigInteger("123456789012345678901234568"),
-                new BigInteger("-1250000124326904597090347547457")
-                };
-
-        for (BigInteger value : values) {
-            String expected = value.toString();
-            assertEquals(expected, MAPPER.writeValueAsString(value));
-        }
-    }
-
-    public void testNumbersAsString() throws Exception
-    {
-        assertEquals(aposToQuotes("{'value':'3'}"), MAPPER.writeValueAsString(new IntAsString()));
-        assertEquals(aposToQuotes("{'value':'4'}"), MAPPER.writeValueAsString(new LongAsString()));
-        assertEquals(aposToQuotes("{'value':'-0.5'}"), MAPPER.writeValueAsString(new DoubleAsString()));
-    }
-
-    public void testConfigOverridesForNumbers() throws Exception
-    {
-        ObjectMapper mapper = new ObjectMapper();
-        mapper.configOverride(Integer.TYPE) // for `int`
-            .setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.STRING));
-        assertEquals(aposToQuotes("{'i':'3'}"),
-                mapper.writeValueAsString(new IntWrapper(3)));
-    }
-}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/SerializationFeaturesTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/SerializationFeaturesTest.java
index e408561..edaefcf 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/SerializationFeaturesTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/SerializationFeaturesTest.java
@@ -3,13 +3,9 @@
 import java.io.*;
 import java.util.*;
 
-import com.fasterxml.jackson.annotation.*;
-import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
-
 import com.fasterxml.jackson.core.JsonGenerator;
 
 import com.fasterxml.jackson.databind.*;
-import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
 
 /**
  * Unit tests for checking handling of some of {@link MapperFeature}s
@@ -18,52 +14,6 @@
 public class SerializationFeaturesTest
     extends BaseMapTest
 {
-    /**
-     * Class with one explicitly defined getter, one name-based
-     * auto-detectable getter.
-     */
-    static class GetterClass
-    {
-        @JsonProperty("x") public int getX() { return -2; }
-        public int getY() { return 1; }
-    }
-
-    /**
-     * Another test-class that explicitly disables auto-detection
-     */
-    @JsonAutoDetect(getterVisibility=Visibility.NONE)
-    static class DisabledGetterClass
-    {
-        @JsonProperty("x") public int getX() { return -2; }
-        public int getY() { return 1; }
-    }
-
-    /**
-     * Another test-class that explicitly enables auto-detection
-     */
-    @JsonAutoDetect(isGetterVisibility=Visibility.NONE)
-    static class EnabledGetterClass
-    {
-        @JsonProperty("x") public int getX() { return -2; }
-        public int getY() { return 1; }
-
-        // not auto-detected, since "is getter" auto-detect disabled
-        public boolean isOk() { return true; }
-    }
-
-    /**
-     * One more: only detect "isXxx", not "getXXX"
-     */
-    @JsonAutoDetect(getterVisibility=Visibility.NONE)
-    static class EnabledIsGetterClass
-    {
-        // Won't be auto-detected any more
-        public int getY() { return 1; }
-
-        // but this will be
-        public boolean isOk() { return true; }
-    }
-
     static class CloseableBean implements Closeable
     {
         public int a = 3;
@@ -83,82 +33,12 @@
         public StringListBean(Collection<String> v) { values = v; }
     }
 
-    static class TCls {
-        @JsonProperty("groupname")
-        private String groupname;
-
-        public void setName(String str) {
-            this.groupname = str;
-        }
-        public String getName() {
-            return groupname;
-        }
-    }
-
     /*
     /**********************************************************
     /* Test methods
     /**********************************************************
      */
 
-    public void testGlobalAutoDetection() throws IOException
-    {
-        // First: auto-detection enabled (default):
-        ObjectMapper m = new ObjectMapper();
-        Map<String,Object> result = writeAndMap(m, new GetterClass());
-        assertEquals(2, result.size());
-        assertEquals(Integer.valueOf(-2), result.get("x"));
-        assertEquals(Integer.valueOf(1), result.get("y"));
-
-        // Then auto-detection disabled. But note: we MUST create a new
-        // mapper, since old version of serializer may be cached by now
-        m = new ObjectMapper();
-        m.configure(MapperFeature.AUTO_DETECT_GETTERS, false);
-        result = writeAndMap(m, new GetterClass());
-        assertEquals(1, result.size());
-        assertTrue(result.containsKey("x"));
-    }
-
-    public void testPerClassAutoDetection() throws IOException
-    {
-        // First: class-level auto-detection disabling
-        ObjectMapper m = new ObjectMapper();
-        Map<String,Object> result = writeAndMap(m, new DisabledGetterClass());
-        assertEquals(1, result.size());
-        assertTrue(result.containsKey("x"));
-
-        // And then class-level auto-detection enabling, should override defaults
-        m.configure(MapperFeature.AUTO_DETECT_GETTERS, false);
-        result = writeAndMap(m, new EnabledGetterClass());
-        assertEquals(2, result.size());
-        assertTrue(result.containsKey("x"));
-        assertTrue(result.containsKey("y"));
-    }
-
-    public void testPerClassAutoDetectionForIsGetter() throws IOException
-    {
-        ObjectMapper m = new ObjectMapper();
-        // class level should override
-        m.configure(MapperFeature.AUTO_DETECT_GETTERS, true);
-        m.configure(MapperFeature.AUTO_DETECT_IS_GETTERS, false);
-         Map<String,Object> result = writeAndMap(m, new EnabledIsGetterClass());
-        assertEquals(1, result.size());
-        assertTrue(result.containsKey("ok"));
-        assertEquals(Boolean.TRUE, result.get("ok"));
-    }
-
-    // Simple test verifying that chainable methods work ok...
-    public void testConfigChainability()
-    {
-        ObjectMapper m = new ObjectMapper();
-        assertTrue(m.isEnabled(MapperFeature.AUTO_DETECT_SETTERS));
-        assertTrue(m.isEnabled(MapperFeature.AUTO_DETECT_GETTERS));
-        m.configure(MapperFeature.AUTO_DETECT_SETTERS, false)
-            .configure(MapperFeature.AUTO_DETECT_GETTERS, false);
-        assertFalse(m.isEnabled(MapperFeature.AUTO_DETECT_SETTERS));
-        assertFalse(m.isEnabled(MapperFeature.AUTO_DETECT_GETTERS));
-    }
-
     // Test for [JACKSON-282]
     @SuppressWarnings("resource")
     public void testCloseCloseable() throws IOException
@@ -258,7 +138,7 @@
         HashSet<Long> longs = new HashSet<Long>();
         longs.add(42L);
         assertEquals("42", writer.writeValueAsString(longs));
-        // [Issue#180]
+        // [databind#180]
         final String EXP_STRINGS = "{\"values\":\"foo\"}";
         assertEquals(EXP_STRINGS, writer.writeValueAsString(new StringListBean(Collections.singletonList("foo"))));
 
@@ -268,29 +148,24 @@
         
         // arrays:
         assertEquals("true", writer.writeValueAsString(new boolean[] { true }));
+        assertEquals("[true,false]", writer.writeValueAsString(new boolean[] { true, false }));
         assertEquals("true", writer.writeValueAsString(new Boolean[] { Boolean.TRUE }));
+
+        assertEquals("3", writer.writeValueAsString(new short[] { 3 }));
+        assertEquals("[3,2]", writer.writeValueAsString(new short[] { 3, 2 }));
+        
         assertEquals("3", writer.writeValueAsString(new int[] { 3 }));
+        assertEquals("[3,2]", writer.writeValueAsString(new int[] { 3, 2 }));
+
+        assertEquals("1", writer.writeValueAsString(new long[] { 1L }));
+        assertEquals("[-1,4]", writer.writeValueAsString(new long[] { -1L, 4L }));
+
+        assertEquals("0.5", writer.writeValueAsString(new double[] { 0.5 }));
+        assertEquals("[0.5,2.5]", writer.writeValueAsString(new double[] { 0.5, 2.5 }));
+
+        assertEquals("0.5", writer.writeValueAsString(new float[] { 0.5f }));
+        assertEquals("[0.5,2.5]", writer.writeValueAsString(new float[] { 0.5f, 2.5f }));
+        
         assertEquals(quote("foo"), writer.writeValueAsString(new String[] { "foo" }));
     }
-
-    public void testVisibilityFeatures() throws Exception
-    {
-        ObjectMapper om = new ObjectMapper();
-        // Only use explicitly specified values to be serialized/deserialized (i.e., JSONProperty).
-        om.configure(MapperFeature.AUTO_DETECT_FIELDS, false);
-        om.configure(MapperFeature.AUTO_DETECT_GETTERS, false);
-        om.configure(MapperFeature.AUTO_DETECT_SETTERS, false);
-        om.configure(MapperFeature.AUTO_DETECT_IS_GETTERS, false);
-        om.configure(MapperFeature.USE_GETTERS_AS_SETTERS, false);
-        om.configure(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS, true);
-        om.configure(MapperFeature.INFER_PROPERTY_MUTATORS, false);
-        om.configure(MapperFeature.USE_ANNOTATIONS, true);
-
-        JavaType javaType = om.getTypeFactory().constructType(TCls.class);        
-        BeanDescription desc = (BeanDescription) om.getSerializationConfig().introspect(javaType);
-        List<BeanPropertyDefinition> props = desc.findProperties();
-        if (props.size() != 1) {
-            fail("Should find 1 property, not "+props.size()+"; properties = "+props);
-        }
-    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestArraySerialization.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestArraySerialization.java
index ee47809..1aa8bd5 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/TestArraySerialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestArraySerialization.java
@@ -73,8 +73,10 @@
 
     public void testStringArray() throws Exception
     {
-        String json = MAPPER.writeValueAsString(new String[] { "a", "\"foo\"", null });
-        assertEquals("[\"a\",\"\\\"foo\\\"\",null]", json);
+        assertEquals("[\"a\",\"\\\"foo\\\"\",null]",
+                MAPPER.writeValueAsString(new String[] { "a", "\"foo\"", null }));
+        assertEquals("[]",
+                MAPPER.writeValueAsString(new String[] { }));
     }
 
     public void testDoubleArray() throws Exception
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestCustomSerializers.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestCustomSerializers.java
index 12e2219..1feeba6 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/TestCustomSerializers.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestCustomSerializers.java
@@ -22,19 +22,14 @@
 /**
  * Tests for verifying various issues with custom serializers.
  */
+@SuppressWarnings("serial")
 public class TestCustomSerializers extends BaseMapTest
 {
-    /*
-    /**********************************************************
-    /* Helper beans
-    /**********************************************************
-     */
-
     static class ElementSerializer extends JsonSerializer<Element>
     {
         @Override
-        public void serialize(Element value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
-            jgen.writeString("element");
+        public void serialize(Element value, JsonGenerator gen, SerializerProvider provider) throws IOException, JsonProcessingException {
+            gen.writeString("element");
         }
     }
     
@@ -49,7 +44,6 @@
     /**
      * Trivial simple custom escape definition set.
      */
-    @SuppressWarnings("serial")
     static class CustomEscapes extends CharacterEscapes
     {
         private final int[] _asciiEscapes;
@@ -110,8 +104,7 @@
             prop = o;
         }
     }
-    
-    @SuppressWarnings("serial")
+
     static class ParentClassSerializer
         extends StdScalarSerializer<Object>
     {
@@ -127,7 +120,32 @@
             gen.writeString(desc+"/"+value);
         }
     }
-    
+
+    static class UCStringSerializer extends StdScalarSerializer<String>
+    {
+        public UCStringSerializer() { super(String.class); }
+
+        @Override
+        public void serialize(String value, JsonGenerator gen,
+                SerializerProvider provider) throws IOException {
+            gen.writeString(value.toUpperCase());
+        }
+    }
+
+    // IMPORTANT: must associate serializer via property annotations
+    protected static class StringListWrapper
+    {
+        @JsonSerialize(contentUsing=UCStringSerializer.class)
+        public List<String> list;
+
+        public StringListWrapper(String... values) {
+            list = new ArrayList<>();
+            for (String value : values) {
+                list.add(value);
+            }
+        }
+    }
+
     /*
     /**********************************************************
     /* Unit tests
@@ -156,12 +174,13 @@
 
         module.addSerializer(Collection.class, new JsonSerializer<Collection>() {
             @Override
-            public void serialize(Collection value, JsonGenerator jgen, SerializerProvider provider)
-                    throws IOException, JsonProcessingException {
+            public void serialize(Collection value, JsonGenerator gen, SerializerProvider provider)
+                    throws IOException
+            {
                 if (value.size() != 0) {
-                    collectionSerializer.serialize(value, jgen, provider);
+                    collectionSerializer.serialize(value, gen, provider);
                 } else {
-                    jgen.writeNull();
+                    gen.writeNull();
                 }
             }
         });
@@ -169,7 +188,7 @@
         assertEquals("null", mapper.writeValueAsString(new ArrayList<Object>()));
     }
 
-    // [Issue#87]: delegating serializer
+    // [databind#87]: delegating serializer
     public void testDelegating() throws Exception
     {
         ObjectMapper mapper = new ObjectMapper();
@@ -189,7 +208,7 @@
         assertEquals("{\"x\":3,\"y\":7}", mapper.writeValueAsString(new Immutable()));
     }
 
-    // [Issue#215]: Allow registering CharacterEscapes via ObjectWriter
+    // [databind#215]: Allow registering CharacterEscapes via ObjectWriter
     public void testCustomEscapes() throws Exception
     {
         assertEquals(quote("foo\\u0062\\Ar"),
@@ -207,4 +226,29 @@
         assertEquals(aposToQuotes("{'prop':'Issue631Bean/42'}"),
                 MAPPER.writeValueAsString(new Issue631Bean(42)));
     }
+
+    public void testWithCustomElements() throws Exception
+    {
+        // First variant that uses per-property override
+        StringListWrapper wr = new StringListWrapper("a", null, "b");
+        assertEquals(aposToQuotes("{'list':['A',null,'B']}"),
+                MAPPER.writeValueAsString(wr));
+
+        // and then per-type registration
+        
+        SimpleModule module = new SimpleModule("test", Version.unknownVersion());
+        module.addSerializer(String.class, new UCStringSerializer());
+        ObjectMapper mapper = new ObjectMapper()
+                .registerModule(module);
+
+        assertEquals(quote("FOOBAR"), mapper.writeValueAsString("foobar"));
+        assertEquals(aposToQuotes("['FOO',null]"),
+                mapper.writeValueAsString(new String[] { "foo", null }));
+
+        List<String> list = Arrays.asList("foo", null);
+        assertEquals(aposToQuotes("['FOO',null]"), mapper.writeValueAsString(list));
+
+        Set<String> set = new LinkedHashSet<String>(Arrays.asList("foo", null));
+        assertEquals(aposToQuotes("['FOO',null]"), mapper.writeValueAsString(set));
+    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestIterable.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestIterable.java
index 48c2e30..2d49d3a 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/TestIterable.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestIterable.java
@@ -1,7 +1,6 @@
 package com.fasterxml.jackson.databind.ser;
 
 import java.io.IOException;
-import java.io.StringWriter;
 import java.util.*;
 
 import com.fasterxml.jackson.core.JsonGenerator;
@@ -27,7 +26,8 @@
             return _ints.iterator();
         }
     }
-    // [JACKSON-689]
+
+    @JsonSerialize(typing=JsonSerialize.Typing.STATIC)
     static class BeanWithIterable {
         private final ArrayList<String> values = new ArrayList<String>();
         {
@@ -37,6 +37,15 @@
         public Iterable<String> getValues() { return values; }
     }
 
+    static class BeanWithIterator {
+        private final ArrayList<String> values = new ArrayList<String>();
+        {
+            values.add("itValue");
+        }
+
+        public Iterator<String> getValues() { return values.iterator(); }
+    }
+    
     static class IntIterable implements Iterable<Integer>
     {
         @Override
@@ -96,37 +105,53 @@
     /**********************************************************
      */
 
-    private final static ObjectMapper MAPPER = new ObjectMapper();
-    
+    private final ObjectMapper MAPPER = new ObjectMapper();
+
+    private final ObjectMapper STATIC_MAPPER = new ObjectMapper();
+    {
+        STATIC_MAPPER.enable(MapperFeature.USE_STATIC_TYPING);
+    }
+
     public void testIterator() throws IOException
     {
-        StringWriter sw = new StringWriter();
         ArrayList<Integer> l = new ArrayList<Integer>();
         l.add(1);
+        l.add(null);
         l.add(-9);
         l.add(0);
-        MAPPER.writeValue(sw, l.iterator());
-        assertEquals("[1,-9,0]", sw.toString().trim());
+        
+        assertEquals("[1,null,-9,0]", MAPPER.writeValueAsString(l.iterator()));
+        l.clear();
+        assertEquals("[]", MAPPER.writeValueAsString(l.iterator()));
     }
 
     public void testIterable() throws IOException
     {
-        StringWriter sw = new StringWriter();
-        MAPPER.writeValue(sw, new IterableWrapper(new int[] { 1, 2, 3 }));
-        assertEquals("[1,2,3]", sw.toString().trim());
+        assertEquals("[1,2,3]",
+                MAPPER.writeValueAsString(new IterableWrapper(new int[] { 1, 2, 3 })));
     }
 
-    // [JACKSON-689], [JACKSON-876]
     public void testWithIterable() throws IOException
     {
-        // 689:
         assertEquals("{\"values\":[\"value\"]}",
-                MAPPER.writeValueAsString(new BeanWithIterable()));
-        // 876:
+                STATIC_MAPPER.writeValueAsString(new BeanWithIterable()));
         assertEquals("[1,2,3]",
-                MAPPER.writeValueAsString(new IntIterable()));
+                STATIC_MAPPER.writeValueAsString(new IntIterable()));
     }
     
+    public void testWithIterator() throws IOException
+    {
+        assertEquals("{\"values\":[\"itValue\"]}",
+                STATIC_MAPPER.writeValueAsString(new BeanWithIterator()));
+
+        // [databind#1977]
+        ArrayList<Number> numbersList = new ArrayList<>();
+        numbersList.add(1);
+        numbersList.add(0.25);
+        String json = MAPPER.writeValueAsString(numbersList.iterator());
+        assertEquals("[1,0.25]", json);
+    }
+
     // [databind#358]
     public void testIterable358() throws Exception {
         String json = MAPPER.writeValueAsString(new B());
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonSerialize.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonSerialize.java
index 01cabf2..483ccb2 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonSerialize.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonSerialize.java
@@ -148,6 +148,7 @@
     {
         try {
             serializeAsString(MAPPER, new BrokenClass());
+            fail("Should not succeed");
         } catch (Exception e) {
             verifyException(e, "types not related");
         }
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestKeySerializers.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestKeySerializers.java
index ccd674b..dd97392 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/TestKeySerializers.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestKeySerializers.java
@@ -21,6 +21,26 @@
         }
     }
 
+    public static class NullKeySerializer extends JsonSerializer<Object>
+    {
+        private String _null;
+        public NullKeySerializer(String s) { _null = s; }
+        @Override
+        public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
+            gen.writeFieldName(_null);
+        }
+    }
+
+    public static class NullValueSerializer extends JsonSerializer<Object>
+    {
+        private String _null;
+        public NullValueSerializer(String s) { _null = s; }
+        @Override
+        public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
+            gen.writeString(_null);
+        }
+    }
+    
     public static class NotKarlBean
     {
         public Map<String,Integer> map = new HashMap<String,Integer>();
@@ -138,7 +158,7 @@
     // Test custom key serializer for enum
     public void testCustomForEnum() throws IOException
     {
-        // can not use shared mapper as we are registering a module
+        // cannot use shared mapper as we are registering a module
         final ObjectMapper mapper = new ObjectMapper();
         SimpleModule mod = new SimpleModule("test");
         mod.addKeySerializer(ABC.class, new ABCKeySerializer());
@@ -148,6 +168,19 @@
         assertEquals("{\"stuff\":{\"xxxB\":\"bar\"}}", json);
     }
 
+    public void testCustomNullSerializers() throws IOException
+    {
+        final ObjectMapper mapper = new ObjectMapper();
+        mapper.getSerializerProvider().setNullKeySerializer(new NullKeySerializer("NULL-KEY"));
+        mapper.getSerializerProvider().setNullValueSerializer(new NullValueSerializer("NULL"));
+        Map<String,Integer> input = new HashMap<>();
+        input.put(null, 3);
+        String json = mapper.writeValueAsString(input);
+        assertEquals("{\"NULL-KEY\":3}", json);
+        json = mapper.writeValueAsString(new Object[] { 1, null, true });
+        assertEquals("[1,\"NULL\",true]", json);
+    }
+    
     public void testCustomEnumInnerMapKey() throws Exception {
         Map<Outer, Object> outerMap = new HashMap<Outer, Object>();
         Map<ABC, Map<String, String>> map = new EnumMap<ABC, Map<String, String>>(ABC.class);
@@ -190,6 +223,7 @@
     }
 
     // [databind#838]
+    @SuppressWarnings("deprecation")
     public void testUnWrappedMapWithKeySerializer() throws Exception{
         SimpleModule mod = new SimpleModule("test");
         mod.addKeySerializer(ABC.class, new ABCKeySerializer());
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestMapSerialization.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestMapSerialization.java
index a220237..26cabd6 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/TestMapSerialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestMapSerialization.java
@@ -15,7 +15,7 @@
 @SuppressWarnings("serial")
 public class TestMapSerialization extends BaseMapTest
 {
-    @JsonSerialize(using=MapSerializer.class)    
+    @JsonSerialize(using=PseudoMapSerializer.class)    
     static class PseudoMap extends LinkedHashMap<String,String>
     {
         public PseudoMap(String... values) {
@@ -25,7 +25,7 @@
         }
     }
 
-    static class MapSerializer extends JsonSerializer<Map<String,String>>
+    static class PseudoMapSerializer extends JsonSerializer<Map<String,String>>
     {
         @Override
         public void serialize(Map<String,String> value,
@@ -36,16 +36,6 @@
         }
     }
 
-    // For [JACKSON-574]
-    static class DefaultKeySerializer extends JsonSerializer<Object>
-    {
-        @Override
-        public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException
-        {
-            jgen.writeFieldName("DEFAULT:"+value);
-        }
-    }
-    
     // [databind#335]
     static class MapOrderingBean {
         @JsonPropertyOrder(alphabetic=true)
@@ -93,29 +83,6 @@
         }
     }
 
-    // for [databind#47]
-    public static class Wat
-    {
-        private final String wat;
-
-        @JsonCreator
-        Wat(String wat) {
-            this.wat = wat;
-        }
-
-        @JsonValue
-        public String getWat() {
-            return wat;
-        }
-
-        @Override
-        public String toString() {
-            return "(String)[Wat: " + wat + "]";
-        }
-    }
-
-    static class WatMap extends HashMap<Wat,Boolean> { }
-
     // for [databind#691]
     @JsonTypeInfo(use=JsonTypeInfo.Id.NAME)
     @JsonTypeName("mymap")
@@ -147,7 +114,7 @@
     }
 
     // problems with map entries, values
-    public void testMapKeyValueSerialization() throws IOException
+    public void testMapKeySetValuesSerialization() throws IOException
     {
         Map<String,String> map = new HashMap<String,String>();
         map.put("a", "b");
@@ -167,16 +134,6 @@
         assertEquals("[\"f\"]", MAPPER.writeValueAsString(map.values()));
     }
 
-    // For [JACKSON-574]
-    public void testDefaultKeySerializer() throws IOException
-    {
-        ObjectMapper m = new ObjectMapper();
-        m.getSerializerProvider().setDefaultKeySerializer(new DefaultKeySerializer());
-        Map<String,String> map = new HashMap<String,String>();
-        map.put("a", "b");
-        assertEquals("{\"DEFAULT:a\":\"b\"}", m.writeValueAsString(map));
-    }
-
     // sort Map entries by key
     public void testOrderByKey() throws IOException
     {
@@ -242,26 +199,6 @@
         assertEquals(aposToQuotes("{'value':{'answer':42}}"), json);
     }
 
-    // [databind#47]
-    public void testMapJsonValueKey47() throws Exception
-    {
-        WatMap input = new WatMap();
-        input.put(new Wat("3"), true);
-
-        ObjectMapper mapper = new ObjectMapper();
-        String json = mapper.writeValueAsString(input);
-        assertEquals(aposToQuotes("{'3':true}"), json);
-    }    
-
-    // [databind#682]
-    public void testClassKey() throws IOException
-    {
-        Map<Class<?>,Integer> map = new LinkedHashMap<Class<?>,Integer>();
-        map.put(String.class, 2);
-        String json = MAPPER.writeValueAsString(map);
-        assertEquals(aposToQuotes("{'java.lang.String':2}"), json);
-    }
-
     // [databind#691]
     public void testNullJsonMapping691() throws Exception
     {
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestRootType.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestRootType.java
index 0795bfe..56af44d 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/TestRootType.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestRootType.java
@@ -56,7 +56,7 @@
         public int a = 3;
     }
 
-    // [Issue#412]
+    // [databind#412]
     static class TestCommandParent {
         public String uuid;
         public int type;
@@ -190,14 +190,13 @@
         assertEquals("456", mapper.writerFor(Long.TYPE).writeValueAsString(Long.valueOf(456L)));
     }
 
-    // [JACKSON-630] also, allow annotation to define root name
     public void testRootNameAnnotation() throws Exception
     {
         String json = WRAP_ROOT_MAPPER.writeValueAsString(new WithRootName());
         assertEquals("{\"root\":{\"a\":3}}", json);
     }
 
-    // [Issue#412]
+    // [databind#412]
     public void testRootNameWithExplicitType() throws Exception
     {
         TestCommandChild cmd = new TestCommandChild();
@@ -207,6 +206,6 @@
         ObjectWriter writer = WRAP_ROOT_MAPPER.writerFor(TestCommandParent.class);
         String json =  writer.writeValueAsString(cmd);
 
-        assertEquals(json, "{\"TestCommandParent\":{\"uuid\":\"1234\",\"type\":1}}");
+        assertEquals("{\"TestCommandParent\":{\"uuid\":\"1234\",\"type\":1}}", json);
     }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestSimpleAtomicTypes.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestSimpleAtomicTypes.java
deleted file mode 100644
index afce72b..0000000
--- a/src/test/java/com/fasterxml/jackson/databind/ser/TestSimpleAtomicTypes.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package com.fasterxml.jackson.databind.ser;
-
-import java.util.concurrent.atomic.*;
-
-import com.fasterxml.jackson.databind.BaseMapTest;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-
-/**
- * Unit tests for verifying serialization of simple basic non-structured
- * types; primitives (and/or their wrappers), Strings.
- */
-public class TestSimpleAtomicTypes
-    extends BaseMapTest
-{
-    static class UCStringWrapper {
-        @JsonSerialize(contentUsing=UpperCasingSerializer.class)
-        public AtomicReference<String> value;
-
-        public UCStringWrapper(String s) { value = new AtomicReference<String>(s); }
-    }
-
-    /*
-    /**********************************************************
-    /* Test methods
-    /**********************************************************
-     */
-
-    private final ObjectMapper MAPPER = objectMapper();
-    
-    public void testAtomicBoolean() throws Exception
-    {
-        assertEquals("true", MAPPER.writeValueAsString(new AtomicBoolean(true)));
-        assertEquals("false", MAPPER.writeValueAsString(new AtomicBoolean(false)));
-    }
-
-    public void testAtomicInteger() throws Exception
-    {
-        assertEquals("1", MAPPER.writeValueAsString(new AtomicInteger(1)));
-        assertEquals("-9", MAPPER.writeValueAsString(new AtomicInteger(-9)));
-    }
-
-    public void testAtomicLong() throws Exception
-    {
-        assertEquals("0", MAPPER.writeValueAsString(new AtomicLong(0)));
-    }
-
-    public void testAtomicReference() throws Exception
-    {
-        String[] strs = new String[] { "abc" };
-        assertEquals("[\"abc\"]", MAPPER.writeValueAsString(new AtomicReference<String[]>(strs)));
-    }
-
-    public void testCustomSerializer() throws Exception
-    {
-        final String VALUE = "fooBAR";
-        String json = MAPPER.writeValueAsString(new UCStringWrapper(VALUE));
-        assertEquals(json, aposToQuotes("{'value':'FOOBAR'}"));
-    }
-}
diff --git a/src/test/java/com/fasterxml/jackson/databind/filter/IgnorePropsForSerTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/filter/IgnorePropsForSerTest.java
similarity index 78%
rename from src/test/java/com/fasterxml/jackson/databind/filter/IgnorePropsForSerTest.java
rename to src/test/java/com/fasterxml/jackson/databind/ser/filter/IgnorePropsForSerTest.java
index a8342bd..73c34a4 100644
--- a/src/test/java/com/fasterxml/jackson/databind/filter/IgnorePropsForSerTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/filter/IgnorePropsForSerTest.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.filter;
+package com.fasterxml.jackson.databind.ser.filter;
 
 import java.util.*;
 
@@ -63,6 +63,25 @@
         }
     }
 
+    // for [databind#1060]
+    static class IgnoreForListValuesXY {
+        @JsonIgnoreProperties({ "x" })
+        public List<XY> coordinates;
+
+        public IgnoreForListValuesXY() {
+            coordinates = Arrays.asList(new XY());
+        }
+    }
+
+    static class IgnoreForListValuesXYZ {
+        @JsonIgnoreProperties({ "y" })
+        public List<XYZ> coordinates;
+
+        public IgnoreForListValuesXYZ() {
+            coordinates = Arrays.asList(new XYZ());
+        }
+    }    
+
     /*
     /****************************************************************
     /* Unit tests
@@ -129,4 +148,17 @@
             .setIgnorals(JsonIgnoreProperties.Value.forIgnoredProperties("x"));
         assertEquals("{\"y\":3}", mapper.writeValueAsString(new Point(2, 3)));
     }
+
+    // for [databind#1060]
+    // Ensure that `@JsonIgnoreProperties` applies to POJOs within lists, too
+    public void testIgnoreForListValues() throws Exception
+    {
+        // should apply to elements
+        assertEquals(aposToQuotes("{'coordinates':[{'y':2}]}"),
+                MAPPER.writeValueAsString(new IgnoreForListValuesXY()));
+
+        // and combine values too
+        assertEquals(aposToQuotes("{'coordinates':[{'z':3}]}"),
+                MAPPER.writeValueAsString(new IgnoreForListValuesXYZ()));
+    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/filter/JsonInclude1327Test.java b/src/test/java/com/fasterxml/jackson/databind/ser/filter/JsonInclude1327Test.java
similarity index 96%
rename from src/test/java/com/fasterxml/jackson/databind/filter/JsonInclude1327Test.java
rename to src/test/java/com/fasterxml/jackson/databind/ser/filter/JsonInclude1327Test.java
index cf11a4d..bac6df4 100644
--- a/src/test/java/com/fasterxml/jackson/databind/filter/JsonInclude1327Test.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/filter/JsonInclude1327Test.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.filter;
+package com.fasterxml.jackson.databind.ser.filter;
 
 import java.util.*;
 
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/filter/JsonIncludeArrayTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/filter/JsonIncludeArrayTest.java
new file mode 100644
index 0000000..4a48cf7
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/filter/JsonIncludeArrayTest.java
@@ -0,0 +1,127 @@
+package com.fasterxml.jackson.databind.ser.filter;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+
+public class JsonIncludeArrayTest extends BaseMapTest
+{
+    static class NonEmptyByteArray {
+        @JsonInclude(JsonInclude.Include.NON_EMPTY)
+        public byte[] value;
+
+        public NonEmptyByteArray(byte... v) { value = v; }
+    }
+
+    static class NonEmptyShortArray {
+        @JsonInclude(JsonInclude.Include.NON_EMPTY)
+        public short[] value;
+
+        public NonEmptyShortArray(short... v) { value = v; }
+    }
+
+    static class NonEmptyCharArray {
+        @JsonInclude(JsonInclude.Include.NON_EMPTY)
+        public char[] value;
+
+        public NonEmptyCharArray(char... v) { value = v; }
+    }
+    
+    static class NonEmptyIntArray {
+        @JsonInclude(JsonInclude.Include.NON_EMPTY)
+        public int[] value;
+
+        public NonEmptyIntArray(int... v) { value = v; }
+    }
+
+    static class NonEmptyLongArray {
+        @JsonInclude(JsonInclude.Include.NON_EMPTY)
+        public long[] value;
+
+        public NonEmptyLongArray(long... v) { value = v; }
+    }
+
+    static class NonEmptyBooleanArray {
+        @JsonInclude(JsonInclude.Include.NON_EMPTY)
+        public boolean[] value;
+
+        public NonEmptyBooleanArray(boolean... v) { value = v; }
+    }
+
+    static class NonEmptyDoubleArray {
+        @JsonInclude(JsonInclude.Include.NON_EMPTY)
+        public double[] value;
+
+        public NonEmptyDoubleArray(double... v) { value = v; }
+    }
+
+    static class NonEmptyFloatArray {
+        @JsonInclude(JsonInclude.Include.NON_EMPTY)
+        public float[] value;
+
+        public NonEmptyFloatArray(float... v) { value = v; }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    final private ObjectMapper MAPPER = new ObjectMapper();
+
+    public void testByteArray() throws IOException
+    {
+        assertEquals("{}", MAPPER.writeValueAsString(new NonEmptyByteArray()));
+    }
+
+    public void testShortArray() throws IOException
+    {
+        assertEquals("{}", MAPPER.writeValueAsString(new NonEmptyShortArray()));
+        assertEquals("{\"value\":[1]}", MAPPER.writeValueAsString(new NonEmptyShortArray((short) 1)));
+    }
+
+    public void testCharArray() throws IOException
+    {
+        assertEquals("{}", MAPPER.writeValueAsString(new NonEmptyCharArray()));
+        // by default considered to be serialized as String
+        assertEquals("{\"value\":\"ab\"}", MAPPER.writeValueAsString(new NonEmptyCharArray('a', 'b')));
+        // but can force as sparse (real) array too
+        assertEquals("{\"value\":[\"a\",\"b\"]}", MAPPER
+                .writer().with(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS)
+                .writeValueAsString(new NonEmptyCharArray('a', 'b')));
+    }
+
+    public void testIntArray() throws IOException
+    {
+        assertEquals("{}", MAPPER.writeValueAsString(new NonEmptyIntArray()));
+        assertEquals("{\"value\":[2]}", MAPPER.writeValueAsString(new NonEmptyIntArray(2)));
+    }
+
+    public void testLongArray() throws IOException
+    {
+        assertEquals("{}", MAPPER.writeValueAsString(new NonEmptyLongArray()));
+        assertEquals("{\"value\":[3,4]}", MAPPER.writeValueAsString(new NonEmptyLongArray(3, 4)));
+    }
+
+    public void testBooleanArray() throws IOException
+    {
+        assertEquals("{}", MAPPER.writeValueAsString(new NonEmptyBooleanArray()));
+        assertEquals("{\"value\":[true,false]}", MAPPER.writeValueAsString(new NonEmptyBooleanArray(true,false)));
+    }
+
+    public void testDoubleArray() throws IOException
+    {
+        assertEquals("{}", MAPPER.writeValueAsString(new NonEmptyDoubleArray()));
+        assertEquals("{\"value\":[0.25,-1.0]}", MAPPER.writeValueAsString(new NonEmptyDoubleArray(0.25,-1.0)));
+    }
+
+    public void testFloatArray() throws IOException
+    {
+        assertEquals("{}", MAPPER.writeValueAsString(new NonEmptyFloatArray()));
+        assertEquals("{\"value\":[0.5]}", MAPPER.writeValueAsString(new NonEmptyFloatArray(0.5f)));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/filter/JsonIncludeCollectionTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/filter/JsonIncludeCollectionTest.java
new file mode 100644
index 0000000..699a194
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/filter/JsonIncludeCollectionTest.java
@@ -0,0 +1,39 @@
+package com.fasterxml.jackson.databind.ser.filter;
+
+import java.util.Arrays;
+import java.util.EnumSet;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class JsonIncludeCollectionTest extends BaseMapTest
+{
+    static class NonEmptyEnumSet {
+        @JsonInclude(JsonInclude.Include.NON_EMPTY)
+        public EnumSet<ABC> v;
+
+        public NonEmptyEnumSet(ABC...values) {
+            if (values.length == 0) {
+                v = EnumSet.noneOf(ABC.class);
+            } else {
+                v = EnumSet.copyOf(Arrays.asList(values));
+            }
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    final private ObjectMapper MAPPER = new ObjectMapper();
+
+    public void testEnumSet() throws Exception
+    {
+        assertEquals("{}", MAPPER.writeValueAsString(new NonEmptyEnumSet()));
+        assertEquals("{\"v\":[\"B\"]}", MAPPER.writeValueAsString(new NonEmptyEnumSet(ABC.B)));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/filter/JsonIncludeCustomTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/filter/JsonIncludeCustomTest.java
new file mode 100644
index 0000000..0e3b0f0
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/filter/JsonIncludeCustomTest.java
@@ -0,0 +1,102 @@
+package com.fasterxml.jackson.databind.ser.filter;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
+
+// Tests for [databind#888]
+public class JsonIncludeCustomTest extends BaseMapTest
+{
+    static class FooFilter {
+        @Override
+        public boolean equals(Object other) {
+            if (other == null) { // do NOT filter out nulls
+                return false;
+            }
+            // in fact, only filter out exact String "foo"
+            return "foo".equals(other);
+        }
+    }
+
+    // for testing prob with `equals(null)` which SHOULD be allowed
+    static class BrokenFilter {
+        @Override
+        public boolean equals(Object other) {
+            /*String str = */ other.toString();
+            return false;
+        }
+    }
+    
+    static class FooBean {
+        @JsonInclude(value=JsonInclude.Include.CUSTOM,
+                valueFilter=FooFilter.class)
+        public String value;
+
+        public FooBean(String v) { value = v; }
+    }
+
+    static class FooMapBean {
+        @JsonInclude(content=JsonInclude.Include.CUSTOM,
+                contentFilter=FooFilter.class)
+        public Map<String,String> stuff = new LinkedHashMap<String,String>();
+
+        public FooMapBean add(String key, String value) {
+            stuff.put(key, value);
+            return this;
+        }
+    }
+
+    static class BrokenBean {
+        @JsonInclude(value=JsonInclude.Include.CUSTOM,
+                valueFilter=BrokenFilter.class)
+        public String value;
+
+        public BrokenBean(String v) { value = v; }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods, success
+    /**********************************************************
+     */
+
+    final private ObjectMapper MAPPER = new ObjectMapper();
+
+    public void testSimpleCustomFilter() throws Exception
+    {
+        assertEquals(aposToQuotes("{'value':'x'}"), MAPPER.writeValueAsString(new FooBean("x")));
+        assertEquals("{}", MAPPER.writeValueAsString(new FooBean("foo")));
+    }
+
+    public void testCustomFilterWithMap() throws Exception
+    {
+        FooMapBean input = new FooMapBean()
+                .add("a", "1")
+                .add("b", "foo")
+                .add("c", "2");
+        
+        assertEquals(aposToQuotes("{'stuff':{'a':'1','c':'2'}}"), MAPPER.writeValueAsString(input));
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods, fail handling
+    /**********************************************************
+     */
+    
+    public void testBrokenFilter() throws Exception
+    {
+        try {
+            String json = MAPPER.writeValueAsString(new BrokenBean("foo"));
+            fail("Should not pass, produced: "+json);
+        } catch (InvalidDefinitionException e) {
+            verifyException(e, "Problem determining whether filter of type");
+            verifyException(e, "filter out `null`");
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/filter/JsonIncludeOverrideTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/filter/JsonIncludeOverrideTest.java
new file mode 100644
index 0000000..1a8dbc5
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/filter/JsonIncludeOverrideTest.java
@@ -0,0 +1,191 @@
+package com.fasterxml.jackson.databind.ser.filter;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * Unit tests for checking that overridden settings for
+ * {@link com.fasterxml.jackson.databind.annotation.JsonSerialize#include} annotation property work
+ * as expected.
+ */
+public class JsonIncludeOverrideTest
+    extends BaseMapTest
+{
+    @JsonPropertyOrder({"list", "map"})
+    static class EmptyListMapBean
+    {
+        public List<String> list = Collections.emptyList();
+
+        public Map<String,String> map = Collections.emptyMap();
+    }
+
+    @JsonInclude(JsonInclude.Include.ALWAYS)
+    @JsonPropertyOrder({"num", "annotated", "plain"})
+    static class MixedTypeAlwaysBean
+    {
+        @JsonInclude(JsonInclude.Include.USE_DEFAULTS)
+        public Integer num = null;
+
+        @JsonInclude(JsonInclude.Include.NON_NULL)
+        public String annotated = null;
+
+        public String plain = null;
+    }
+
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    @JsonPropertyOrder({"num", "annotated", "plain"})
+    static class MixedTypeNonNullBean
+    {
+        @JsonInclude(JsonInclude.Include.USE_DEFAULTS)
+        public Integer num = null;
+
+        @JsonInclude(JsonInclude.Include.ALWAYS)
+        public String annotated = null;
+
+        public String plain = null;
+    }
+
+    public void testPropConfigOverridesForInclude() throws IOException
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        // First, with defaults, both included:
+        JsonIncludeOverrideTest.EmptyListMapBean empty = new JsonIncludeOverrideTest.EmptyListMapBean();
+        assertEquals(aposToQuotes("{'list':[],'map':{}}"),
+                mapper.writeValueAsString(empty));
+
+        // and then change inclusion criteria for either
+        mapper = new ObjectMapper();
+        mapper.configOverride(Map.class)
+            .setInclude(JsonInclude.Value.construct(JsonInclude.Include.NON_EMPTY, null));
+        assertEquals(aposToQuotes("{'list':[]}"),
+                mapper.writeValueAsString(empty));
+
+        mapper = new ObjectMapper();
+        mapper.configOverride(List.class)
+            .setInclude(JsonInclude.Value.construct(JsonInclude.Include.NON_EMPTY, null));
+        assertEquals(aposToQuotes("{'map':{}}"),
+                mapper.writeValueAsString(empty));
+    }
+
+    public void testOverrideForIncludeAsPropertyNonNull() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        // First, with defaults, all but NON_NULL annotated included
+        JsonIncludeOverrideTest.MixedTypeAlwaysBean nullValues = new JsonIncludeOverrideTest.MixedTypeAlwaysBean();
+        assertEquals(aposToQuotes("{'num':null,'plain':null}"),
+                mapper.writeValueAsString(nullValues));
+
+        // and then change inclusion as property criteria for either
+        mapper = new ObjectMapper();
+        mapper.configOverride(String.class)
+                .setIncludeAsProperty(JsonInclude.Value
+                        .construct(JsonInclude.Include.NON_NULL, null));
+        assertEquals("{\"num\":null}",
+                mapper.writeValueAsString(nullValues));
+
+        mapper = new ObjectMapper();
+        mapper.configOverride(Integer.class)
+                .setIncludeAsProperty(JsonInclude.Value
+                        .construct(JsonInclude.Include.NON_NULL, null));
+        assertEquals("{\"plain\":null}",
+                mapper.writeValueAsString(nullValues));
+    }
+
+    public void testOverrideForIncludeAsPropertyAlways() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        // First, with defaults, only ALWAYS annotated included
+        JsonIncludeOverrideTest.MixedTypeNonNullBean nullValues = new JsonIncludeOverrideTest.MixedTypeNonNullBean();
+        assertEquals("{\"annotated\":null}",
+                mapper.writeValueAsString(nullValues));
+
+        // and then change inclusion as property criteria for either
+        mapper = new ObjectMapper();
+        mapper.configOverride(String.class)
+                .setIncludeAsProperty(JsonInclude.Value
+                        .construct(JsonInclude.Include.ALWAYS, null));
+        assertEquals(aposToQuotes("{'annotated':null,'plain':null}"),
+                mapper.writeValueAsString(nullValues));
+
+        mapper = new ObjectMapper();
+        mapper.configOverride(Integer.class)
+                .setIncludeAsProperty(JsonInclude.Value
+                        .construct(JsonInclude.Include.ALWAYS, null));
+        assertEquals(aposToQuotes("{'num':null,'annotated':null}"),
+                mapper.writeValueAsString(nullValues));
+    }
+
+    public void testOverridesForIncludeAndIncludeAsPropertyNonNull() throws Exception
+    {
+        // First, with ALWAYS override on containing bean, all included
+        JsonIncludeOverrideTest.MixedTypeNonNullBean nullValues = new JsonIncludeOverrideTest.MixedTypeNonNullBean();
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.configOverride(JsonIncludeOverrideTest.MixedTypeNonNullBean.class)
+                .setInclude(JsonInclude.Value
+                        .construct(JsonInclude.Include.ALWAYS, null));
+        assertEquals(aposToQuotes("{'num':null,'annotated':null,'plain':null}"),
+                mapper.writeValueAsString(nullValues));
+
+        // and then change inclusion as property criteria for either
+        mapper = new ObjectMapper();
+        mapper.configOverride(JsonIncludeOverrideTest.MixedTypeNonNullBean.class)
+                .setInclude(JsonInclude.Value
+                        .construct(JsonInclude.Include.ALWAYS, null));
+        mapper.configOverride(String.class)
+                .setIncludeAsProperty(JsonInclude.Value
+                        .construct(JsonInclude.Include.NON_NULL, null));
+        assertEquals(aposToQuotes("{'num':null,'annotated':null}"),
+                mapper.writeValueAsString(nullValues));
+
+        mapper = new ObjectMapper();
+        mapper.configOverride(JsonIncludeOverrideTest.MixedTypeNonNullBean.class)
+                .setInclude(JsonInclude.Value
+                        .construct(JsonInclude.Include.ALWAYS, null));
+        mapper.configOverride(Integer.class)
+                .setIncludeAsProperty(JsonInclude.Value
+                        .construct(JsonInclude.Include.NON_NULL, null));
+        assertEquals(aposToQuotes("{'annotated':null,'plain':null}"),
+                mapper.writeValueAsString(nullValues));
+    }
+
+    public void testOverridesForIncludeAndIncludeAsPropertyAlways() throws Exception
+    {
+        // First, with NON_NULL override on containing bean, empty
+        JsonIncludeOverrideTest.MixedTypeAlwaysBean nullValues = new JsonIncludeOverrideTest.MixedTypeAlwaysBean();
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.configOverride(JsonIncludeOverrideTest.MixedTypeAlwaysBean.class)
+                .setInclude(JsonInclude.Value
+                        .construct(JsonInclude.Include.NON_NULL, null));
+        assertEquals("{}",
+                mapper.writeValueAsString(nullValues));
+
+        // and then change inclusion as property criteria for either
+        mapper = new ObjectMapper();
+        mapper.configOverride(JsonIncludeOverrideTest.MixedTypeAlwaysBean.class)
+                .setInclude(JsonInclude.Value
+                        .construct(JsonInclude.Include.NON_NULL, null));
+        mapper.configOverride(String.class)
+                .setIncludeAsProperty(JsonInclude.Value
+                        .construct(JsonInclude.Include.ALWAYS, null));
+        assertEquals("{\"plain\":null}",
+                mapper.writeValueAsString(nullValues));
+
+        mapper = new ObjectMapper();
+        mapper.configOverride(JsonIncludeOverrideTest.MixedTypeAlwaysBean.class)
+                .setInclude(JsonInclude.Value
+                        .construct(JsonInclude.Include.NON_NULL, null));
+        mapper.configOverride(Integer.class)
+                .setIncludeAsProperty(JsonInclude.Value
+                        .construct(JsonInclude.Include.ALWAYS, null));
+        assertEquals("{\"num\":null}",
+                mapper.writeValueAsString(nullValues));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/filter/JsonIncludeTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/filter/JsonIncludeTest.java
similarity index 84%
rename from src/test/java/com/fasterxml/jackson/databind/filter/JsonIncludeTest.java
rename to src/test/java/com/fasterxml/jackson/databind/ser/filter/JsonIncludeTest.java
index a5aa82e..5d18dff 100644
--- a/src/test/java/com/fasterxml/jackson/databind/filter/JsonIncludeTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/filter/JsonIncludeTest.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.filter;
+package com.fasterxml.jackson.databind.ser.filter;
 
 import java.io.IOException;
 import java.util.*;
@@ -121,12 +121,32 @@
         public NonEmptyDouble(double v) { value = v; }
     }
 
-    @JsonPropertyOrder({"list", "map"})
-    static class EmptyListMapBean
-    {
-        public List<String> list = Collections.emptyList();
+    static class NonEmpty<T> {
+        @JsonInclude(JsonInclude.Include.NON_EMPTY)
+        public T value;
 
-        public Map<String,String> map = Collections.emptyMap();
+        public NonEmpty(T v) { value = v; }
+    }
+
+    static class NonEmptyDate extends NonEmpty<Date> {
+        public NonEmptyDate(Date v) { super(v); }
+    }
+    static class NonEmptyCalendar extends NonEmpty<Calendar> {
+        public NonEmptyCalendar(Calendar v) { super(v); }
+    }
+
+    static class NonDefault<T> {
+        @JsonInclude(JsonInclude.Include.NON_DEFAULT)
+        public T value;
+
+        public NonDefault(T v) { value = v; }
+    }
+
+    static class NonDefaultDate extends NonDefault<Date> {
+        public NonDefaultDate(Date v) { super(v); }
+    }
+    static class NonDefaultCalendar extends NonDefault<Calendar> {
+        public NonDefaultCalendar(Calendar v) { super(v); }
     }
 
     // [databind#1351]
@@ -176,7 +196,7 @@
 
     /*
     /**********************************************************
-    /* Unit tests
+    /* Test methods
     /**********************************************************
      */
 
@@ -281,34 +301,11 @@
         assertEquals("{\"value\":1.25}", defMapper.writeValueAsString(new NonEmptyDouble(1.25)));
         assertEquals("{\"value\":0.0}", defMapper.writeValueAsString(new NonEmptyDouble(0.0)));
 
-        
         IntWrapper zero = new IntWrapper(0);
         assertEquals("{\"i\":0}", defMapper.writeValueAsString(zero));
         assertEquals("{\"i\":0}", inclMapper.writeValueAsString(zero));
     }
 
-    public void testPropConfigOverridesForInclude() throws IOException
-    {
-        // First, with defaults, both included:
-        EmptyListMapBean empty = new EmptyListMapBean();
-        assertEquals(aposToQuotes("{'list':[],'map':{}}"),
-                MAPPER.writeValueAsString(empty));
-        ObjectMapper mapper;
-
-        // and then change inclusion criteria for either
-        mapper = new ObjectMapper();
-        mapper.configOverride(Map.class)
-            .setInclude(JsonInclude.Value.construct(JsonInclude.Include.NON_EMPTY, null));
-        assertEquals(aposToQuotes("{'list':[]}"),
-                mapper.writeValueAsString(empty));
-
-        mapper = new ObjectMapper();
-        mapper.configOverride(List.class)
-            .setInclude(JsonInclude.Value.construct(JsonInclude.Include.NON_EMPTY, null));
-        assertEquals(aposToQuotes("{'map':{}}"),
-                mapper.writeValueAsString(empty));
-    }
-
     // [databind#1351], [databind#1417]
     public void testIssue1351() throws Exception
     {
@@ -320,4 +317,27 @@
         assertEquals(aposToQuotes("{}"),
                 mapper.writeValueAsString(new Issue1351NonBean(0)));
     }
+
+    // [databind#1550]
+    public void testInclusionOfDate() throws Exception
+    {
+        final Date input = new Date(0L);
+        assertEquals(aposToQuotes("{'value':0}"), 
+                MAPPER.writeValueAsString(new NonEmptyDate(input)));
+        assertEquals("{}", 
+                MAPPER.writeValueAsString(new NonDefaultDate(input)));
+
+    
+    }
+
+    // [databind#1550]
+    public void testInclusionOfCalendar() throws Exception
+    {
+        final Calendar input = new GregorianCalendar();
+        input.setTimeInMillis(0L);
+        assertEquals(aposToQuotes("{'value':0}"), 
+                MAPPER.writeValueAsString(new NonEmptyCalendar(input)));
+        assertEquals("{}", 
+                MAPPER.writeValueAsString(new NonDefaultCalendar(input)));
+    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/filter/MapInclusionTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/filter/MapInclusionTest.java
new file mode 100644
index 0000000..cdd0a94
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/filter/MapInclusionTest.java
@@ -0,0 +1,83 @@
+package com.fasterxml.jackson.databind.ser.filter;
+
+import java.io.IOException;
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.*;
+
+public class MapInclusionTest extends BaseMapTest
+{
+    static class NoEmptiesMapContainer {
+        @JsonInclude(value=JsonInclude.Include.NON_EMPTY,
+                content=JsonInclude.Include.NON_EMPTY)
+        public Map<String,String> stuff = new LinkedHashMap<String,String>();
+
+        public NoEmptiesMapContainer add(String key, String value) {
+            stuff.put(key, value);
+            return this;
+        }
+    }
+
+    static class NoNullsMapContainer {
+        @JsonInclude(value=JsonInclude.Include.NON_NULL,
+                content=JsonInclude.Include.NON_NULL)
+        public Map<String,String> stuff = new LinkedHashMap<String,String>();
+
+        public NoNullsMapContainer add(String key, String value) {
+            stuff.put(key, value);
+            return this;
+        }
+    }
+
+    static class NoNullsNotEmptyMapContainer {
+        @JsonInclude(value=JsonInclude.Include.NON_EMPTY,
+                content=JsonInclude.Include.NON_NULL)
+        public Map<String,String> stuff = new LinkedHashMap<String,String>();
+
+        public NoNullsNotEmptyMapContainer add(String key, String value) {
+            stuff.put(key, value);
+            return this;
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    final private ObjectMapper MAPPER = objectMapper();
+
+    // [databind#588]
+    public void testNonEmptyValueMapViaProp() throws IOException
+    {
+        String json = MAPPER.writeValueAsString(new NoEmptiesMapContainer()
+            .add("a", null)
+            .add("b", ""));
+        assertEquals(aposToQuotes("{}"), json);
+    }
+
+    public void testNoNullsMap() throws IOException
+    {
+        NoNullsMapContainer input = new NoNullsMapContainer()
+                .add("a", null)
+                .add("b", "");
+        String json = MAPPER.writeValueAsString(input);
+        assertEquals(aposToQuotes("{'stuff':{'b':''}}"), json);
+    }
+
+    public void testNonEmptyNoNullsMap() throws IOException
+    {
+        NoNullsNotEmptyMapContainer input = new NoNullsNotEmptyMapContainer()
+                .add("a", null)
+                .add("b", "");
+        String json = MAPPER.writeValueAsString(input);
+        assertEquals(aposToQuotes("{'stuff':{'b':''}}"), json);
+
+        json = MAPPER.writeValueAsString(new NoNullsNotEmptyMapContainer()
+                .add("a", null)
+                .add("b", null));
+        assertEquals(aposToQuotes("{}"), json);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/filter/NullSerializationTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/filter/NullSerializationTest.java
similarity index 91%
rename from src/test/java/com/fasterxml/jackson/databind/filter/NullSerializationTest.java
rename to src/test/java/com/fasterxml/jackson/databind/ser/filter/NullSerializationTest.java
index dc74036..d1cf083 100644
--- a/src/test/java/com/fasterxml/jackson/databind/filter/NullSerializationTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/filter/NullSerializationTest.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.filter;
+package com.fasterxml.jackson.databind.ser.filter;
 
 import java.io.*;
 
@@ -118,17 +118,6 @@
         assertEquals("{\"a\":\"foobar\"}", m.writeValueAsString(root));
     }
 
-    /* 14-Oct-2013, tatu: Support for annotating classes is not
-     *   implemented yet.
-     */
-/*    
-    public void testNullSerializerViaClass() throws Exception
-    {
-        assertEquals("[\"foobar\"]",
-                MAPPER.writeValueAsString(new NullValuedType[] { new NullValuedType() }));
-    }
-    */
-
     public void testNullSerializerForProperty() throws Exception
     {
         assertEquals("{\"a\":\"foobar\"}", MAPPER.writeValueAsString(new BeanWithNullProps()));
diff --git a/src/test/java/com/fasterxml/jackson/databind/filter/TestAnyGetterFiltering.java b/src/test/java/com/fasterxml/jackson/databind/ser/filter/TestAnyGetterFiltering.java
similarity index 63%
rename from src/test/java/com/fasterxml/jackson/databind/filter/TestAnyGetterFiltering.java
rename to src/test/java/com/fasterxml/jackson/databind/ser/filter/TestAnyGetterFiltering.java
index e579feb..dadc7e2 100644
--- a/src/test/java/com/fasterxml/jackson/databind/filter/TestAnyGetterFiltering.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/filter/TestAnyGetterFiltering.java
@@ -1,9 +1,11 @@
-package com.fasterxml.jackson.databind.filter;
+package com.fasterxml.jackson.databind.ser.filter;
 
 import java.util.*;
 
 import com.fasterxml.jackson.annotation.*;
+
 import com.fasterxml.jackson.core.JsonGenerator;
+
 import com.fasterxml.jackson.databind.BaseMapTest;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.SerializerProvider;
@@ -51,6 +53,32 @@
         }
     }
 
+    // [databind#1655]
+    @JsonFilter("CustomFilter")
+    static class OuterObject {
+         public int getExplicitProperty() {
+              return 42;
+         }
+
+         @JsonAnyGetter
+         public Map<String, Object> getAny() {
+              Map<String, Object> extra = new HashMap<>();
+              extra.put("dynamicProperty", "I will not serialize");
+              return extra;
+         }
+    }
+
+    static class CustomFilter extends SimpleBeanPropertyFilter {
+         @Override
+         public void serializeAsField(Object pojo, JsonGenerator gen, SerializerProvider provider,
+                 PropertyWriter writer) throws Exception
+         {
+             if (pojo instanceof OuterObject) {
+                 writer.serializeAsField(pojo, gen, provider);
+              }
+         }
+    }
+
     /*
     /**********************************************************
     /* Test methods
@@ -72,4 +100,15 @@
         assertEquals(aposToQuotes("{'a':'1','b':'3'}"),
                 MAPPER.writeValueAsString(new AnyBeanWithIgnores()));
     }
+
+    // [databind#1655]
+    public void testAnyGetterPojo1655() throws Exception
+    {
+        FilterProvider filters = new SimpleFilterProvider().addFilter("CustomFilter", new CustomFilter());
+        String json = MAPPER.writer(filters).writeValueAsString(new OuterObject());
+        Map<?,?> stuff = MAPPER.readValue(json, Map.class);
+        if (stuff.size() != 2) {
+            fail("Should have 2 properties, got: "+stuff);
+        }
+   }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestIgnoredTypes.java b/src/test/java/com/fasterxml/jackson/databind/ser/filter/TestIgnoredTypes.java
similarity index 98%
rename from src/test/java/com/fasterxml/jackson/databind/deser/TestIgnoredTypes.java
rename to src/test/java/com/fasterxml/jackson/databind/ser/filter/TestIgnoredTypes.java
index 01b5c51..3279c02 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestIgnoredTypes.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/filter/TestIgnoredTypes.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.deser;
+package com.fasterxml.jackson.databind.ser.filter;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/src/test/java/com/fasterxml/jackson/databind/filter/TestJsonFilter.java b/src/test/java/com/fasterxml/jackson/databind/ser/filter/TestJsonFilter.java
similarity index 97%
rename from src/test/java/com/fasterxml/jackson/databind/filter/TestJsonFilter.java
rename to src/test/java/com/fasterxml/jackson/databind/ser/filter/TestJsonFilter.java
index b607af7..84b057c 100644
--- a/src/test/java/com/fasterxml/jackson/databind/filter/TestJsonFilter.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/filter/TestJsonFilter.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.filter;
+package com.fasterxml.jackson.databind.ser.filter;
 
 import com.fasterxml.jackson.annotation.*;
 
@@ -146,7 +146,7 @@
             MAPPER.writeValueAsString(new Bean());
             fail("Should have failed without configured filter");
         } catch (JsonMappingException e) { // should be resolved to a MappingException (internally may be something else)
-            verifyException(e, "Can not resolve PropertyFilter with id 'RootFilter'");
+            verifyException(e, "Cannot resolve PropertyFilter with id 'RootFilter'");
         }
         
         // but when changing behavior, should work difference
diff --git a/src/test/java/com/fasterxml/jackson/databind/filter/TestMapFiltering.java b/src/test/java/com/fasterxml/jackson/databind/ser/filter/TestMapFiltering.java
similarity index 76%
rename from src/test/java/com/fasterxml/jackson/databind/filter/TestMapFiltering.java
rename to src/test/java/com/fasterxml/jackson/databind/ser/filter/TestMapFiltering.java
index a7a3729..ab2a9a9 100644
--- a/src/test/java/com/fasterxml/jackson/databind/filter/TestMapFiltering.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/filter/TestMapFiltering.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.filter;
+package com.fasterxml.jackson.databind.ser.filter;
 
 import java.io.IOException;
 import java.lang.annotation.*;
@@ -14,6 +14,7 @@
 import com.fasterxml.jackson.databind.ser.*;
 import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
 import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
+import com.fasterxml.jackson.databind.ser.std.MapProperty;
 
 @SuppressWarnings("serial")
 public class TestMapFiltering extends BaseMapTest
@@ -27,7 +28,7 @@
 
     @JsonFilter("filterForMaps")
     static class FilteredBean extends LinkedHashMap<String,Integer> { }
-    
+
     static class MapBean {
         @JsonFilter("filterX")
         @CustomOffset(1)
@@ -41,22 +42,44 @@
         }
     }
 
+    static class MapBeanNoOffset {
+        @JsonFilter("filterX")
+        public Map<String,Integer> values;
+        
+        public MapBeanNoOffset() {
+            values = new LinkedHashMap<String,Integer>();
+            values.put("a", 1);
+            values.put("b", 2);
+            values.put("c", 3);
+        }
+    }
+    
     static class TestMapFilter implements PropertyFilter
     {
         @Override
-        public void serializeAsField(Object value, JsonGenerator jgen,
+        public void serializeAsField(Object bean, JsonGenerator g,
                 SerializerProvider provider, PropertyWriter writer)
             throws Exception
         {
             String name = writer.getName();
+
+            // sanity checks
+            assertNotNull(writer.getType());
+            assertEquals(name, writer.getFullName().getSimpleName());
+
             if (!"a".equals(name)) {
                 return;
             }
             CustomOffset n = writer.findAnnotation(CustomOffset.class);
             int offset = (n == null) ? 0 : n.value();
-            Integer I = offset + ((Integer) value).intValue();
 
-            writer.serializeAsField(I, jgen, provider);
+            // 12-Jun-2017, tatu: With 2.9, `value` is the surrounding POJO, so
+            //    need to do casting
+            MapProperty prop = (MapProperty) writer;
+            Integer old = (Integer) prop.getValue();
+            prop.setValue(Integer.valueOf(offset + old.intValue()));
+
+            writer.serializeAsField(bean, g, provider);
         }
 
         @Override
@@ -70,15 +93,12 @@
         @Deprecated
         public void depositSchemaProperty(PropertyWriter writer,
                 ObjectNode propertiesNode, SerializerProvider provider)
-                throws JsonMappingException {
-            
-        }
+            throws JsonMappingException { }
 
         @Override
         public void depositSchemaProperty(PropertyWriter writer,
                 JsonObjectFormatVisitor objectVisitor,
-                SerializerProvider provider) throws JsonMappingException {
-        }
+                SerializerProvider provider) throws JsonMappingException { }
     }
 
     // [databind#527]
@@ -180,6 +200,10 @@
         String json = MAPPER.writer(prov).writeValueAsString(new MapBean());
         // a=1 should become a=2
         assertEquals(aposToQuotes("{'values':{'a':2}}"), json);
+
+        // and then one without annotation as contrast
+        json = MAPPER.writer(prov).writeValueAsString(new MapBeanNoOffset());
+        assertEquals(aposToQuotes("{'values':{'a':1}}"), json);
     }
 
     // [databind#527]
@@ -212,6 +236,7 @@
         assertEquals(aposToQuotes("{'a':'foo'}"), json);
     }
 
+    @SuppressWarnings("deprecation")
     public void testMapNullSerialization() throws IOException
     {
         ObjectMapper m = new ObjectMapper();
@@ -220,7 +245,9 @@
         // by default, should output null-valued entries:
         assertEquals("{\"a\":null}", m.writeValueAsString(map));
         // but not if explicitly asked not to (note: config value is dynamic here)
-        m.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false);
+
+        m = new ObjectMapper();        
+        m.disable(SerializationFeature.WRITE_NULL_MAP_VALUES);
         assertEquals("{}", m.writeValueAsString(map));
     }
 
@@ -240,4 +267,31 @@
             .add("b", null)));
         assertEquals(aposToQuotes("{}"), json);
     }
+
+    public void testMapViaGlobalNonEmpty() throws Exception
+    {
+        // basic Map<String,String> subclass:
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.setDefaultPropertyInclusion(JsonInclude.Value.empty()
+                .withContentInclusion(JsonInclude.Include.NON_EMPTY));
+        assertEquals(aposToQuotes("{'a':'b'}"), mapper.writeValueAsString(
+                new StringMap497()
+                    .add("x", "")
+                    .add("a", "b")
+                    ));
+    }
+
+    public void testMapViaTypeOverride() throws Exception
+    {
+        // basic Map<String,String> subclass:
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.configOverride(Map.class)
+            .setInclude(JsonInclude.Value.empty()
+                .withContentInclusion(JsonInclude.Include.NON_EMPTY));
+        assertEquals(aposToQuotes("{'a':'b'}"), mapper.writeValueAsString(
+                new StringMap497()
+                    .add("foo", "")
+                    .add("a", "b")
+                    ));
+    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/filter/TestSimpleSerializationIgnore.java b/src/test/java/com/fasterxml/jackson/databind/ser/filter/TestSimpleSerializationIgnore.java
similarity index 98%
rename from src/test/java/com/fasterxml/jackson/databind/filter/TestSimpleSerializationIgnore.java
rename to src/test/java/com/fasterxml/jackson/databind/ser/filter/TestSimpleSerializationIgnore.java
index 5a580f7..92c7054 100644
--- a/src/test/java/com/fasterxml/jackson/databind/filter/TestSimpleSerializationIgnore.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/filter/TestSimpleSerializationIgnore.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.filter;
+package com.fasterxml.jackson.databind.ser.filter;
 
 import java.util.*;
 
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/jdk/AtomicTypeSerializationTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/AtomicTypeSerializationTest.java
new file mode 100644
index 0000000..19f2890
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/AtomicTypeSerializationTest.java
@@ -0,0 +1,138 @@
+package com.fasterxml.jackson.databind.ser.jdk;
+
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.concurrent.atomic.*;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+/**
+ * Unit tests for verifying serialization of {@link java.util.concurrent.atomic.AtomicReference}
+ * and other atomic types, via various settings.
+ */
+public class AtomicTypeSerializationTest
+    extends BaseMapTest
+{
+    static class UCStringWrapper {
+        @JsonSerialize(contentUsing=UpperCasingSerializer.class)
+        public AtomicReference<String> value;
+
+        public UCStringWrapper(String s) { value = new AtomicReference<String>(s); }
+    }
+
+    // [datatypes-java8#17]
+    @JsonPropertyOrder({ "date1", "date2", "date" })
+    static class ContextualOptionals
+    {
+        public AtomicReference<Date> date;
+
+        @JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy+MM+dd")
+        public AtomicReference<Date> date1;
+
+        @JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy*MM*dd")
+        public AtomicReference<Date> date2;
+    }
+
+    // [databind#1673]
+    static class ContainerA {
+        public AtomicReference<Strategy> strategy =
+                new AtomicReference<>((Strategy) new Foo(42));
+    }
+
+    static class ContainerB {
+        public AtomicReference<List<Strategy>> strategy;
+        {
+            List<Strategy> list = new ArrayList<>();
+            list.add(new Foo(42));
+            strategy = new AtomicReference<>(list);
+        }
+    }
+
+    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
+    @JsonSubTypes({ @JsonSubTypes.Type(name = "Foo", value = Foo.class) })
+    interface Strategy { }
+
+    static class Foo implements Strategy {
+        public int foo;
+
+        @JsonCreator
+        Foo(@JsonProperty("foo") int foo) {
+            this.foo = foo;
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = objectMapper();
+    
+    public void testAtomicBoolean() throws Exception
+    {
+        assertEquals("true", MAPPER.writeValueAsString(new AtomicBoolean(true)));
+        assertEquals("false", MAPPER.writeValueAsString(new AtomicBoolean(false)));
+    }
+
+    public void testAtomicInteger() throws Exception
+    {
+        assertEquals("1", MAPPER.writeValueAsString(new AtomicInteger(1)));
+        assertEquals("-9", MAPPER.writeValueAsString(new AtomicInteger(-9)));
+    }
+
+    public void testAtomicLong() throws Exception
+    {
+        assertEquals("0", MAPPER.writeValueAsString(new AtomicLong(0)));
+    }
+
+    public void testAtomicReference() throws Exception
+    {
+        String[] strs = new String[] { "abc" };
+        assertEquals("[\"abc\"]", MAPPER.writeValueAsString(new AtomicReference<String[]>(strs)));
+    }
+
+    public void testCustomSerializer() throws Exception
+    {
+        final String VALUE = "fooBAR";
+        String json = MAPPER.writeValueAsString(new UCStringWrapper(VALUE));
+        assertEquals(json, aposToQuotes("{'value':'FOOBAR'}"));
+    }
+
+    public void testContextualAtomicReference() throws Exception
+    {
+        SimpleDateFormat df = new SimpleDateFormat("yyyy/MM/dd");
+        df.setTimeZone(TimeZone.getTimeZone("UTC"));
+        final ObjectMapper mapper = objectMapper();
+        mapper.setDateFormat(df);
+        ContextualOptionals input = new ContextualOptionals();
+        input.date = new AtomicReference<>(new Date(0L));
+        input.date1 = new AtomicReference<>(new Date(0L));
+        input.date2 = new AtomicReference<>(new Date(0L));
+        final String json = mapper.writeValueAsString(input);
+        assertEquals(aposToQuotes(
+                "{'date1':'1970+01+01','date2':'1970*01*01','date':'1970/01/01'}"),
+                json);
+    }
+
+    // [databind#1673]
+    public void testPolymorphicReferenceSimple() throws Exception
+    {
+        final String EXPECTED = "{\"type\":\"Foo\",\"foo\":42}";
+        String json = MAPPER.writeValueAsString(new ContainerA());
+        assertEquals("{\"strategy\":" + EXPECTED + "}", json);
+    }
+
+    // [databind#1673]
+    public void testPolymorphicReferenceListOf() throws Exception
+    {
+        final String EXPECTED = "{\"type\":\"Foo\",\"foo\":42}";
+        // Reproduction of issue seen with scala.Option and java8 Optional types:
+        // https://github.com/FasterXML/jackson-module-scala/issues/346#issuecomment-336483326
+        String json = MAPPER.writeValueAsString(new ContainerB());
+        assertEquals("{\"strategy\":[" + EXPECTED + "]}", json);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestCollectionSerialization.java b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/CollectionSerializationTest.java
similarity index 85%
rename from src/test/java/com/fasterxml/jackson/databind/ser/TestCollectionSerialization.java
rename to src/test/java/com/fasterxml/jackson/databind/ser/jdk/CollectionSerializationTest.java
index 59267de..1d09d74 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/TestCollectionSerialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/CollectionSerializationTest.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.ser;
+package com.fasterxml.jackson.databind.ser.jdk;
 
 import java.io.*;
 import java.util.*;
@@ -6,9 +6,10 @@
 import com.fasterxml.jackson.annotation.*;
 import com.fasterxml.jackson.core.*;
 import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 
-public class TestCollectionSerialization
+public class CollectionSerializationTest
     extends BaseMapTest
 {
     enum Key { A, B, C };
@@ -66,6 +67,18 @@
         public String[] empty = new String[0];
     }
 
+    static class StaticListWrapper {
+        protected List<String> list;
+
+        public StaticListWrapper(String ... v) {
+            list = new ArrayList<String>(Arrays.asList(v));
+        }
+        protected StaticListWrapper() { }
+        
+        public List<String> getList( ) { return list; }
+        public void setList(List<String> l) { list = l; }
+    }
+
     /*
     /**********************************************************
     /* Test methods
@@ -227,14 +240,15 @@
         assertNull(result.get("map"));
     }
 
-    // Test [JACKSON-220]
     public void testListSerializer() throws IOException
     {
-        assertEquals("\"[ab, cd, ef]\"",
+        assertEquals(quote("[ab, cd, ef]"),
                 MAPPER.writeValueAsString(new PseudoList("ab", "cd", "ef")));
+        assertEquals(quote("[]"),
+                MAPPER.writeValueAsString(new PseudoList()));
     }
 
-    // [JACKSON-254]
+    @SuppressWarnings("deprecation")
     public void testEmptyListOrArray() throws IOException
     {
         // by default, empty lists serialized normally
@@ -250,4 +264,19 @@
         assertEquals("{}", m.writeValueAsString(list));
         assertEquals("{}", m.writeValueAsString(array));
     }
+
+    public void testStaticList() throws IOException
+    {
+        // First: au naturel
+        StaticListWrapper w = new StaticListWrapper("a", "b", "c");
+        String json = MAPPER.writeValueAsString(w);
+        assertEquals(aposToQuotes("{'list':['a','b','c']}"), json);
+
+        // but then with default typing
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.enableDefaultTyping(DefaultTyping.NON_FINAL);
+        json = mapper.writeValueAsString(w);
+        assertEquals(aposToQuotes(String.format("['%s',{'list':['%s',['a','b','c']]}]",
+                w.getClass().getName(), w.list.getClass().getName())), json);
+    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/DateSerializationTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/DateSerializationTest.java
similarity index 77%
rename from src/test/java/com/fasterxml/jackson/databind/ser/DateSerializationTest.java
rename to src/test/java/com/fasterxml/jackson/databind/ser/jdk/DateSerializationTest.java
index aff0b09..0760575 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/DateSerializationTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/DateSerializationTest.java
@@ -1,11 +1,24 @@
-package com.fasterxml.jackson.databind.ser;
+package com.fasterxml.jackson.databind.ser.jdk;
 
-import java.io.*;
-import java.text.*;
-import java.util.*;
+import java.io.IOException;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+
+import org.junit.Assert;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
-import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectWriter;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.databind.util.StdDateFormat;
 
 public class DateSerializationTest
     extends BaseMapTest
@@ -26,17 +39,6 @@
         public DateAsNumberBean(long l) { date = new java.util.Date(l); }
     }
 
-    static class SqlDateAsDefaultBean {
-        public java.sql.Date date;
-        public SqlDateAsDefaultBean(long l) { date = new java.sql.Date(l); }
-    }
-
-    static class SqlDateAsNumberBean {
-        @JsonFormat(shape=JsonFormat.Shape.NUMBER)
-        public java.sql.Date date;
-        public SqlDateAsNumberBean(long l) { date = new java.sql.Date(l); }
-    }
-
     static class DateAsStringBean {
         @JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd")
         public Date date;
@@ -114,9 +116,42 @@
     {
         ObjectMapper mapper = new ObjectMapper();
         mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
-        // let's hit epoch start
-        String json = mapper.writeValueAsString(new Date(0L));
-        assertEquals("\"1970-01-01T00:00:00.000+0000\"", json);
+
+        serialize( mapper, judate(1970, 1, 1,  02, 00, 00, 0, "GMT+2"), "1970-01-01T00:00:00.000+0000");
+		serialize( mapper, judate(1970, 1, 1,  00, 00, 00, 0, "UTC"),   "1970-01-01T00:00:00.000+0000");
+    }
+    
+    /**
+     * Use a default TZ other than UTC. Dates must be serialized using that TZ.
+     */
+    public void testDateISO8601_customTZ() throws IOException
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
+        mapper.setTimeZone(TimeZone.getTimeZone("GMT+2"));
+
+        serialize( mapper, judate(1970, 1, 1,  00, 00, 00, 0, "GMT+2"), "1970-01-01T00:00:00.000+0200");
+        serialize( mapper, judate(1970, 1, 1,  00, 00, 00, 0, "UTC"),   "1970-01-01T02:00:00.000+0200");
+    }
+
+    /**
+     * Configure the StdDateFormat to serialize TZ offset with a colon between hours and minutes
+     *
+     * See [databind#1744]
+     */
+    public void testDateISO8601_colonInTZ() throws IOException
+    {
+        StdDateFormat dateFormat = new StdDateFormat();
+        assertFalse(dateFormat.isColonIncludedInTimeZone());
+        dateFormat = dateFormat.withColonInTimeZone(true);
+        assertTrue(dateFormat.isColonIncludedInTimeZone());
+
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
+        mapper.setDateFormat(dateFormat);
+        
+        serialize( mapper, judate(1970, 1, 1,  02, 00, 00, 0, "GMT+2"), "1970-01-01T00:00:00.000+00:00");
+        serialize( mapper, judate(1970, 1, 1,  00, 00, 00, 0, "UTC"),   "1970-01-01T00:00:00.000+00:00");
     }
 
     public void testDateOther() throws IOException
@@ -125,40 +160,9 @@
         DateFormat df = new SimpleDateFormat("yyyy-MM-dd'X'HH:mm:ss");
         mapper.setDateFormat(df);
         mapper.setTimeZone(TimeZone.getTimeZone("PST"));
+
         // let's hit epoch start, offset by a bit
-        assertEquals(quote("1969-12-31X16:00:00"), mapper.writeValueAsString(new Date(0L)));
-    }
-
-    @SuppressWarnings("deprecation")
-    public void testSqlDate() throws IOException
-    {
-        // use date 1999-04-01 (note: months are 0-based, use constant)
-        java.sql.Date date = new java.sql.Date(99, Calendar.APRIL, 1);
-        assertEquals(quote("1999-04-01"), MAPPER.writeValueAsString(date));
-
-        java.sql.Date date0 = new java.sql.Date(0L);
-        assertEquals(aposToQuotes("{'date':'"+date0.toString()+"'}"),
-                MAPPER.writeValueAsString(new SqlDateAsDefaultBean(0L)));
-
-        // but may explicitly force timestamp too
-        assertEquals(aposToQuotes("{'date':0}"), MAPPER.writeValueAsString(new SqlDateAsNumberBean(0L)));
-    }
-
-    public void testSqlTime() throws IOException
-    {
-        java.sql.Time time = new java.sql.Time(0L);
-        // not 100% sure what we should expect wrt timezone, but what serializes
-        // does use is quite simple:
-        assertEquals(quote(time.toString()), MAPPER.writeValueAsString(time));
-    }
-
-    public void testSqlTimestamp() throws IOException
-    {
-        java.sql.Timestamp input = new java.sql.Timestamp(0L);
-        // just should produce same output as standard `java.util.Date`:
-        Date altTnput = new Date(0L);
-        assertEquals(MAPPER.writeValueAsString(altTnput),
-                MAPPER.writeValueAsString(input));
+        serialize( mapper, judate(1970, 1, 1,  00, 00, 00, 0, "UTC"), "1969-12-31X16:00:00");
     }
 
     public void testTimeZone() throws IOException
@@ -243,19 +247,18 @@
         mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd/HH:mm z"));
         mapper.setTimeZone(TimeZone.getTimeZone("PST"));
         mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
-        String json = mapper.writeValueAsString(new Date(0));
+
         // pacific time is GMT-8; so midnight becomes 16:00 previous day:
-        assertEquals(quote("1969-12-31/16:00 PST"), json);
+        serialize( mapper, judate(1969, 12, 31, 16, 00, 00, 00, "PST"), "1969-12-31/16:00 PST");
 
         // Let's also verify that Locale won't matter too much...
         mapper.setLocale(Locale.FRANCE);
-        json = mapper.writeValueAsString(new Date(0));
-        assertEquals(quote("1969-12-31/16:00 PST"), json);
+        serialize( mapper, judate(1969, 12, 31, 16, 00, 00, 00, "PST"), "1969-12-31/16:00 PST");
 
         // Also: should be able to dynamically change timezone:
         ObjectWriter w = mapper.writer();
         w = w.with(TimeZone.getTimeZone("EST"));
-        json = w.writeValueAsString(new Date(0));
+        String json = w.writeValueAsString(new Date(0));
         assertEquals(quote("1969-12-31/19:00 EST"), json);
     }
 
@@ -315,4 +318,18 @@
         String json = mapper.writeValueAsString(new DateAsDefaultBeanWithTimezone(0L));
         assertEquals(aposToQuotes("{'date':'1970-01-01X01:00:00'}"), json);
     }
+
+    private static Date judate(int year, int month, int day, int hour, int minutes, int seconds, int millis, String tz) {
+        Calendar cal = Calendar.getInstance();
+        cal.set(year, month-1, day, hour, minutes, seconds);
+        cal.set(Calendar.MILLISECOND, millis);
+        cal.setTimeZone(TimeZone.getTimeZone(tz));
+
+        return cal.getTime();
+    }
+
+    private void serialize(ObjectMapper mapper, Object date, String expected) throws IOException {
+        String actual = mapper.writeValueAsString(date);
+        Assert.assertEquals(quote(expected), actual);
+    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestJdkTypes.java b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/JDKTypeSerializationTest.java
similarity index 84%
rename from src/test/java/com/fasterxml/jackson/databind/ser/TestJdkTypes.java
rename to src/test/java/com/fasterxml/jackson/databind/ser/jdk/JDKTypeSerializationTest.java
index 9aea068..055fdf8 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/TestJdkTypes.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/JDKTypeSerializationTest.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.ser;
+package com.fasterxml.jackson.databind.ser.jdk;
 
 import java.io.*;
 import java.math.BigDecimal;
@@ -9,6 +9,7 @@
 import java.util.*;
 import java.util.regex.Pattern;
 
+import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.databind.*;
 
@@ -16,11 +17,17 @@
  * Unit tests for JDK types not covered by other tests (i.e. things
  * that are not Enums, Collections, Maps, or standard Date/Time types)
  */
-public class TestJdkTypes
+public class JDKTypeSerializationTest
     extends com.fasterxml.jackson.databind.BaseMapTest
 {
     private final ObjectMapper MAPPER = objectMapper();
 
+    static class InetAddressBean {
+        public InetAddress value;
+
+        public InetAddressBean(InetAddress i) { value = i; }
+    }
+    
     public void testBigDecimal() throws Exception
     {
         Map<String, Object> map = new HashMap<String, Object>();
@@ -83,19 +90,26 @@
     public void testInetAddress() throws IOException
     {
         assertEquals(quote("127.0.0.1"), MAPPER.writeValueAsString(InetAddress.getByName("127.0.0.1")));
-        assertEquals(quote("ning.com"), MAPPER.writeValueAsString(InetAddress.getByName("ning.com")));
+        InetAddress input = InetAddress.getByName("google.com");
+        assertEquals(quote("google.com"), MAPPER.writeValueAsString(input));
+
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.configOverride(InetAddress.class)
+            .setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.NUMBER));
+        String json = mapper.writeValueAsString(input);
+        assertEquals(quote(input.getHostAddress()), json);
+
+        assertEquals(String.format("{\"value\":\"%s\"}", input.getHostAddress()),
+                mapper.writeValueAsString(new InetAddressBean(input)));
     }
 
     public void testInetSocketAddress() throws IOException
     {
-        assertEquals(
-                quote("127.0.0.1:8080"),
+        assertEquals(quote("127.0.0.1:8080"),
                 MAPPER.writeValueAsString(new InetSocketAddress("127.0.0.1", 8080)));
-        assertEquals(
-                quote("ning.com:6667"),
-                MAPPER.writeValueAsString(new InetSocketAddress("ning.com", 6667)));
-        assertEquals(
-                quote("[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443"),
+        assertEquals(quote("google.com:6667"),
+                MAPPER.writeValueAsString(new InetSocketAddress("google.com", 6667)));
+        assertEquals(quote("[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443"),
                 MAPPER.writeValueAsString(new InetSocketAddress("2001:db8:85a3:8d3:1319:8a2e:370:7348", 443)));
     }
 
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/KeySerializers1679Test.java b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/KeySerializers1679Test.java
similarity index 92%
rename from src/test/java/com/fasterxml/jackson/databind/ser/KeySerializers1679Test.java
rename to src/test/java/com/fasterxml/jackson/databind/ser/jdk/KeySerializers1679Test.java
index ea7e600..2578b84 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/KeySerializers1679Test.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/KeySerializers1679Test.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.ser;
+package com.fasterxml.jackson.databind.ser.jdk;
 
 import java.util.*;
 
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/jdk/MapKeySerializationTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/MapKeySerializationTest.java
new file mode 100644
index 0000000..8731deb
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/MapKeySerializationTest.java
@@ -0,0 +1,103 @@
+package com.fasterxml.jackson.databind.ser.jdk;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+import com.fasterxml.jackson.core.Base64Variants;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.*;
+
+@SuppressWarnings("serial")
+public class MapKeySerializationTest extends BaseMapTest
+{
+    // for [databind#47]
+    public static class Wat
+    {
+        private final String wat;
+
+        @JsonCreator
+        Wat(String wat) {
+            this.wat = wat;
+        }
+
+        @JsonValue
+        public String getWat() {
+            return wat;
+        }
+
+        @Override
+        public String toString() {
+            return "(String)[Wat: " + wat + "]";
+        }
+    }
+
+    static class WatMap extends HashMap<Wat,Boolean> { }
+
+    static class DefaultKeySerializer extends JsonSerializer<Object>
+    {
+        @Override
+        public void serialize(Object value, JsonGenerator g, SerializerProvider provider) throws IOException
+        {
+            g.writeFieldName("DEFAULT:"+value);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    final private ObjectMapper MAPPER = objectMapper();
+
+    // [databind#47]
+    public void testMapJsonValueKey47() throws Exception
+    {
+        WatMap input = new WatMap();
+        input.put(new Wat("3"), true);
+
+        String json = MAPPER.writeValueAsString(input);
+        assertEquals(aposToQuotes("{'3':true}"), json);
+    }    
+
+    // [databind#682]
+    public void testClassKey() throws IOException
+    {
+        Map<Class<?>,Integer> map = new LinkedHashMap<Class<?>,Integer>();
+        map.put(String.class, 2);
+        String json = MAPPER.writeValueAsString(map);
+        assertEquals(aposToQuotes("{'java.lang.String':2}"), json);
+    }
+
+    public void testDefaultKeySerializer() throws IOException
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.getSerializerProvider().setDefaultKeySerializer(new DefaultKeySerializer());
+        Map<String,String> map = new HashMap<String,String>();
+        map.put("a", "b");
+        assertEquals("{\"DEFAULT:a\":\"b\"}", m.writeValueAsString(map));
+    }
+
+    // [databind#1552]
+    public void testMapsWithBinaryKeys() throws Exception
+    {
+        byte[] binary = new byte[] { 1, 2, 3, 4, 5 };
+
+        // First, using wrapper
+        MapWrapper<byte[], String> input = new MapWrapper<>(binary, "stuff");
+        String expBase64 = Base64Variants.MIME.encode(binary);
+        
+        assertEquals(aposToQuotes("{'map':{'"+expBase64+"':'stuff'}}"),
+                MAPPER.writeValueAsString(input));
+
+        // and then dynamically..
+        Map<byte[],String> map = new LinkedHashMap<>();
+        map.put(binary, "xyz");
+        assertEquals(aposToQuotes("{'"+expBase64+"':'xyz'}"),
+                MAPPER.writeValueAsString(map));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/jdk/NumberSerTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/NumberSerTest.java
new file mode 100644
index 0000000..d99eaf4
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/NumberSerTest.java
@@ -0,0 +1,142 @@
+package com.fasterxml.jackson.databind.ser.jdk;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+/**
+ * Unit tests for verifying serialization of simple basic non-structured
+ * types; primitives (and/or their wrappers), Strings.
+ */
+public class NumberSerTest extends BaseMapTest
+{
+    private final ObjectMapper MAPPER = objectMapper();
+
+    static class IntWrapper {
+        public int i;
+        public IntWrapper(int value) { i = value; }
+    }
+
+    static class DoubleWrapper {
+        public double value;
+        public DoubleWrapper(double v) { value = v; }
+    }
+
+    static class BigDecimalWrapper {
+        public BigDecimal value;
+        public BigDecimalWrapper(BigDecimal v) { value = v; }
+    }
+
+    static class IntAsString {
+        @JsonFormat(shape=JsonFormat.Shape.STRING)
+        @JsonProperty("value")
+        public int foo = 3;
+    }
+
+    static class LongAsString {
+        @JsonFormat(shape=JsonFormat.Shape.STRING)
+        public long value = 4;
+    }
+
+    static class DoubleAsString {
+        @JsonFormat(shape=JsonFormat.Shape.STRING)
+        public double value = -0.5;
+    }
+
+    static class BigIntegerAsString {
+        @JsonFormat(shape=JsonFormat.Shape.STRING)
+        public BigInteger value = BigInteger.valueOf(123456L);
+    }
+
+    static class BigDecimalAsString {
+        @JsonFormat(shape=JsonFormat.Shape.STRING)
+        public BigDecimal value = BigDecimal.valueOf(0.25);
+    }
+    
+    static class NumberWrapper {
+        // ensure it will use `Number` as statically force type, when looking for serializer
+        @JsonSerialize(as=Number.class)
+        public Number value;
+
+        public NumberWrapper(Number v) { value = v; }
+    }
+    
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    public void testDouble() throws Exception
+    {
+        double[] values = new double[] {
+            0.0, 1.0, 0.1, -37.01, 999.99, 0.3, 33.3, Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY
+        };
+        for (double d : values) {
+            String expected = String.valueOf(d);
+            if (Double.isNaN(d) || Double.isInfinite(d)) {
+                expected = "\""+d+"\"";
+            }
+            assertEquals(expected, MAPPER.writeValueAsString(Double.valueOf(d)));
+        }
+    }
+
+    public void testBigInteger() throws Exception
+    {
+        BigInteger[] values = new BigInteger[] {
+                BigInteger.ONE, BigInteger.TEN, BigInteger.ZERO,
+                BigInteger.valueOf(1234567890L),
+                new BigInteger("123456789012345678901234568"),
+                new BigInteger("-1250000124326904597090347547457")
+                };
+
+        for (BigInteger value : values) {
+            String expected = value.toString();
+            assertEquals(expected, MAPPER.writeValueAsString(value));
+        }
+    }
+
+    public void testNumbersAsString() throws Exception
+    {
+        assertEquals(aposToQuotes("{'value':'3'}"), MAPPER.writeValueAsString(new IntAsString()));
+        assertEquals(aposToQuotes("{'value':'4'}"), MAPPER.writeValueAsString(new LongAsString()));
+        assertEquals(aposToQuotes("{'value':'-0.5'}"), MAPPER.writeValueAsString(new DoubleAsString()));
+        assertEquals(aposToQuotes("{'value':'0.25'}"), MAPPER.writeValueAsString(new BigDecimalAsString()));
+        assertEquals(aposToQuotes("{'value':'123456'}"), MAPPER.writeValueAsString(new BigIntegerAsString()));
+    }
+
+    public void testConfigOverridesForNumbers() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.configOverride(Integer.TYPE) // for `int`
+            .setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.STRING));
+        mapper.configOverride(Double.TYPE) // for `double`
+            .setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.STRING));
+        mapper.configOverride(BigDecimal.class)
+            .setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.STRING));
+
+        assertEquals(aposToQuotes("{'i':'3'}"),
+                mapper.writeValueAsString(new IntWrapper(3)));
+        assertEquals(aposToQuotes("{'value':'0.75'}"),
+                mapper.writeValueAsString(new DoubleWrapper(0.75)));
+        assertEquals(aposToQuotes("{'value':'-0.5'}"),
+                mapper.writeValueAsString(new BigDecimalWrapper(BigDecimal.valueOf(-0.5))));
+    }
+
+    public void testNumberType() throws Exception
+    {
+        assertEquals(aposToQuotes("{'value':1}"), MAPPER.writeValueAsString(new NumberWrapper(Byte.valueOf((byte) 1))));
+        assertEquals(aposToQuotes("{'value':2}"), MAPPER.writeValueAsString(new NumberWrapper(Short.valueOf((short) 2))));
+        assertEquals(aposToQuotes("{'value':3}"), MAPPER.writeValueAsString(new NumberWrapper(Integer.valueOf(3))));
+        assertEquals(aposToQuotes("{'value':4}"), MAPPER.writeValueAsString(new NumberWrapper(Long.valueOf(4L))));
+        assertEquals(aposToQuotes("{'value':0.5}"), MAPPER.writeValueAsString(new NumberWrapper(Float.valueOf(0.5f))));
+        assertEquals(aposToQuotes("{'value':0.05}"), MAPPER.writeValueAsString(new NumberWrapper(Double.valueOf(0.05))));
+        assertEquals(aposToQuotes("{'value':123}"), MAPPER.writeValueAsString(new NumberWrapper(BigInteger.valueOf(123))));
+        assertEquals(aposToQuotes("{'value':0.025}"), MAPPER.writeValueAsString(new NumberWrapper(BigDecimal.valueOf(0.025))));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/jdk/SqlDateSerializationTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/SqlDateSerializationTest.java
new file mode 100644
index 0000000..2ae4ff8
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/SqlDateSerializationTest.java
@@ -0,0 +1,97 @@
+package com.fasterxml.jackson.databind.ser.jdk;
+
+import java.io.IOException;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+
+// Tests for `java.sql.Date`, `java.sql.Time` and `java.sql.Timestamp`
+public class SqlDateSerializationTest extends BaseMapTest
+{
+    static class SqlDateAsDefaultBean {
+        public java.sql.Date date;
+        public SqlDateAsDefaultBean(long l) { date = new java.sql.Date(l); }
+    }
+
+    static class SqlDateAsNumberBean {
+        @JsonFormat(shape=JsonFormat.Shape.NUMBER)
+        public java.sql.Date date;
+        public SqlDateAsNumberBean(long l) { date = new java.sql.Date(l); }
+    }
+
+    // for [databind#1407]
+    static class Person {
+        @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy.MM.dd")
+        public java.sql.Date dateOfBirth;
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+
+    @SuppressWarnings("deprecation")
+    public void testSqlDate() throws IOException
+    {
+        // use date 1999-04-01 (note: months are 0-based, use constant)
+        final java.sql.Date date99 = new java.sql.Date(99, Calendar.APRIL, 1);
+        final java.sql.Date date0 = new java.sql.Date(0);
+
+        // 11-Oct-2016, tatu: As per [databind#219] we really should use global
+        //   defaults in 2.9, even if this changes behavior.
+
+        assertEquals(String.valueOf(date99.getTime()),
+                MAPPER.writeValueAsString(date99));
+
+        assertEquals(aposToQuotes("{'date':0}"),
+                MAPPER.writeValueAsString(new SqlDateAsDefaultBean(0L)));
+
+        // but may explicitly force timestamp too
+        assertEquals(aposToQuotes("{'date':0}"),
+                MAPPER.writeValueAsString(new SqlDateAsNumberBean(0L)));
+
+        // And also should be able to use String output as need be:
+        ObjectWriter w = MAPPER.writer().without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
+               
+        assertEquals(quote("1999-04-01"), w.writeValueAsString(date99));
+        assertEquals(quote(date0.toString()), w.writeValueAsString(date0));
+        assertEquals(aposToQuotes("{'date':'"+date0.toString()+"'}"),
+                w.writeValueAsString(new SqlDateAsDefaultBean(0L)));
+    }
+
+    public void testSqlTime() throws IOException
+    {
+        java.sql.Time time = new java.sql.Time(0L);
+        // not 100% sure what we should expect wrt timezone, but what serializes
+        // does use is quite simple:
+        assertEquals(quote(time.toString()), MAPPER.writeValueAsString(time));
+    }
+
+    public void testSqlTimestamp() throws IOException
+    {
+        java.sql.Timestamp input = new java.sql.Timestamp(0L);
+        // just should produce same output as standard `java.util.Date`:
+        Date altTnput = new Date(0L);
+        assertEquals(MAPPER.writeValueAsString(altTnput),
+                MAPPER.writeValueAsString(input));
+    }
+    
+    public void testPatternWithSqlDate() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        // `java.sql.Date` applies system default zone (and not UTC)
+        mapper.setTimeZone(TimeZone.getDefault());
+
+        Person i = new Person();
+        i.dateOfBirth = java.sql.Date.valueOf("1980-04-14");
+        assertEquals(aposToQuotes("{'dateOfBirth':'1980.04.14'}"),
+                mapper.writeValueAsString(i));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestUntypedSerialization.java b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/UntypedSerializationTest.java
similarity index 97%
rename from src/test/java/com/fasterxml/jackson/databind/ser/TestUntypedSerialization.java
rename to src/test/java/com/fasterxml/jackson/databind/ser/jdk/UntypedSerializationTest.java
index e62ed96..9117e79 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/TestUntypedSerialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/UntypedSerializationTest.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.ser;
+package com.fasterxml.jackson.databind.ser.jdk;
 
 import java.util.*;
 
@@ -11,7 +11,7 @@
  * "Native" java type mapper; basically that is can properly serialize
  * core JDK objects to JSON.
  */
-public class TestUntypedSerialization
+public class UntypedSerializationTest
     extends BaseMapTest
 {
     public void testFromArray()
diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/BackReference1878Test.java b/src/test/java/com/fasterxml/jackson/databind/struct/BackReference1878Test.java
new file mode 100644
index 0000000..6a86565
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/BackReference1878Test.java
@@ -0,0 +1,30 @@
+package com.fasterxml.jackson.databind.struct;
+
+import com.fasterxml.jackson.annotation.JsonBackReference;
+import com.fasterxml.jackson.annotation.JsonManagedReference;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * @author Reda.Housni-Alaoui
+ */
+public class BackReference1878Test extends BaseMapTest
+{
+    static class Child {
+        @JsonBackReference
+        public Parent b;
+    }
+
+    static class Parent {
+        @JsonManagedReference
+        public Child a;
+    }
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+
+    public void testChildDeserialization() throws Exception {
+        Child child = MAPPER.readValue("{\"b\": {}}", Child.class);
+        assertNotNull(child.b);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/EmptyArrayAsNullTest.java b/src/test/java/com/fasterxml/jackson/databind/struct/EmptyArrayAsNullTest.java
index 5199545..71aba32 100644
--- a/src/test/java/com/fasterxml/jackson/databind/struct/EmptyArrayAsNullTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/EmptyArrayAsNullTest.java
@@ -2,11 +2,8 @@
 
 import java.math.BigDecimal;
 import java.math.BigInteger;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.EnumMap;
-import java.util.Map;
-import java.util.UUID;
+
+import java.util.*;
 
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.*;
@@ -105,14 +102,14 @@
 
     public void testWrapperFromEmptyArray() throws Exception
     {
-//        _testNullWrapper(Boolean.class);
-//        _testNullWrapper(Byte.class);
+        _testNullWrapper(Boolean.class);
+        _testNullWrapper(Byte.class);
         _testNullWrapper(Character.class);
-//        _testNullWrapper(Short.class);
-//        _testNullWrapper(Integer.class);
-//        _testNullWrapper(Long.class);
-//        _testNullWrapper(Float.class);
-//        _testNullWrapper(Double.class);
+        _testNullWrapper(Short.class);
+        _testNullWrapper(Integer.class);
+        _testNullWrapper(Long.class);
+        _testNullWrapper(Float.class);
+        _testNullWrapper(Double.class);
     }
 
     /*
@@ -136,10 +133,8 @@
 
         _testNullWrapper(UUID.class);
 
-        /*
         _testNullWrapper(Date.class);
         _testNullWrapper(Calendar.class);
-        */
     }
 
     /*
diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/FormatFeatureAcceptSingleTest.java b/src/test/java/com/fasterxml/jackson/databind/struct/FormatFeatureAcceptSingleTest.java
new file mode 100644
index 0000000..3aca91e
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/FormatFeatureAcceptSingleTest.java
@@ -0,0 +1,179 @@
+package com.fasterxml.jackson.databind.struct;
+
+import java.util.EnumSet;
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class FormatFeatureAcceptSingleTest extends BaseMapTest
+{
+    static class StringArrayNotAnnoted {
+        public String[] values;
+
+        protected StringArrayNotAnnoted() { }
+        public StringArrayNotAnnoted(String ... v) { values = v; }
+    }
+    
+    static class StringArrayWrapper {
+        @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
+        public String[] values;
+    }
+
+    static class BooleanArrayWrapper {
+        @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
+        public boolean[] values;
+    }
+
+    static class IntArrayWrapper {
+        @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
+        public int[] values;
+    }
+
+    static class LongArrayWrapper {
+        @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
+        public long[] values;
+    }
+
+    static class FloatArrayWrapper {
+        @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
+        public float[] values;
+    }
+    
+    static class DoubleArrayWrapper {
+        @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
+        public double[] values;
+    }
+
+    static class StringListWrapper {
+        @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
+        public List<String> values;
+    }
+
+    static class EnumSetWrapper {
+        @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
+        public EnumSet<ABC> values;
+    }
+    
+    static class RolesInArray {
+        @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
+        public Role[] roles;
+    }
+
+    static class RolesInList {
+        @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
+        public List<Role> roles;
+    }
+
+    static class Role {
+        public String ID;
+        public String Name;
+    }
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+
+    /*
+    /**********************************************************
+    /* Test methods, reading with single-element unwrapping
+    /**********************************************************
+     */
+
+    public void testSingleStringArrayRead() throws Exception {
+        String json = aposToQuotes(
+                "{ 'values': 'first' }");
+        StringArrayWrapper result = MAPPER.readValue(json, StringArrayWrapper.class);
+        assertNotNull(result.values);
+        assertEquals(1, result.values.length);
+        assertEquals("first", result.values[0]);
+
+        // and then without annotation, but with global override
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.configOverride(String[].class).setFormat(JsonFormat.Value.empty()
+                .withFeature(JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY));
+        StringArrayNotAnnoted result2 = mapper.readValue(json, StringArrayNotAnnoted.class);
+        assertNotNull(result2.values);
+        assertEquals(1, result2.values.length);
+        assertEquals("first", result2.values[0]);
+    }
+
+    public void testSingleIntArrayRead() throws Exception {
+        String json = aposToQuotes(
+                "{ 'values': 123 }");
+        IntArrayWrapper result = MAPPER.readValue(json, IntArrayWrapper.class);
+        assertNotNull(result.values);
+        assertEquals(1, result.values.length);
+        assertEquals(123, result.values[0]);
+    }
+
+    public void testSingleLongArrayRead() throws Exception {
+        String json = aposToQuotes(
+                "{ 'values': -205 }");
+        LongArrayWrapper result = MAPPER.readValue(json, LongArrayWrapper.class);
+        assertNotNull(result.values);
+        assertEquals(1, result.values.length);
+        assertEquals(-205L, result.values[0]);
+    }
+
+    public void testSingleBooleanArrayRead() throws Exception {
+        String json = aposToQuotes(
+                "{ 'values': true }");
+        BooleanArrayWrapper result = MAPPER.readValue(json, BooleanArrayWrapper.class);
+        assertNotNull(result.values);
+        assertEquals(1, result.values.length);
+        assertEquals(true, result.values[0]);
+    }
+
+    public void testSingleDoubleArrayRead() throws Exception {
+        String json = aposToQuotes(
+                "{ 'values': -0.5 }");
+        DoubleArrayWrapper result = MAPPER.readValue(json, DoubleArrayWrapper.class);
+        assertNotNull(result.values);
+        assertEquals(1, result.values.length);
+        assertEquals(-0.5, result.values[0]);
+    }
+
+    public void testSingleFloatArrayRead() throws Exception {
+        String json = aposToQuotes(
+                "{ 'values': 0.25 }");
+        FloatArrayWrapper result = MAPPER.readValue(json, FloatArrayWrapper.class);
+        assertNotNull(result.values);
+        assertEquals(1, result.values.length);
+        assertEquals(0.25f, result.values[0]);
+    }
+    
+    public void testSingleElementArrayRead() throws Exception {
+        String json = aposToQuotes(
+                "{ 'roles': { 'Name': 'User', 'ID': '333' } }");
+        RolesInArray response = MAPPER.readValue(json, RolesInArray.class);
+        assertNotNull(response.roles);
+        assertEquals(1, response.roles.length);
+        assertEquals("333", response.roles[0].ID);
+    }
+    
+    public void testSingleStringListRead() throws Exception {
+        String json = aposToQuotes(
+                "{ 'values': 'first' }");
+        StringListWrapper result = MAPPER.readValue(json, StringListWrapper.class);
+        assertNotNull(result.values);
+        assertEquals(1, result.values.size());
+        assertEquals("first", result.values.get(0));
+    }
+
+    public void testSingleElementListRead() throws Exception {
+        String json = aposToQuotes(
+                "{ 'roles': { 'Name': 'User', 'ID': '333' } }");
+        RolesInList response = MAPPER.readValue(json, RolesInList.class);
+        assertNotNull(response.roles);
+        assertEquals(1, response.roles.size());
+        assertEquals("333", response.roles.get(0).ID);
+    }
+
+    public void testSingleEnumSetRead() throws Exception {
+        EnumSetWrapper result = MAPPER.readValue(aposToQuotes("{ 'values': 'B' }"),
+                EnumSetWrapper.class);
+        assertNotNull(result.values);
+        assertEquals(1, result.values.size());
+        assertEquals(ABC.B, result.values.iterator().next());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/FormatFeatureOrderedMapTest.java b/src/test/java/com/fasterxml/jackson/databind/struct/FormatFeatureOrderedMapTest.java
new file mode 100644
index 0000000..3391ff8
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/FormatFeatureOrderedMapTest.java
@@ -0,0 +1,35 @@
+package com.fasterxml.jackson.databind.struct;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Tests for {@link JsonFormat} and specifically <code>JsonFormat.Feature</code>s.
+ */
+public class FormatFeatureOrderedMapTest extends BaseMapTest
+{
+    static class SortedKeysMap {
+        @JsonFormat(with = JsonFormat.Feature.WRITE_SORTED_MAP_ENTRIES)
+        public Map<String,Integer> values = new LinkedHashMap<>();
+
+        protected SortedKeysMap() { }
+
+        public SortedKeysMap put(String key, int value) {
+            values.put(key, value);
+            return this;
+        }
+    }
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+
+    // [databind#1232]: allow forcing sorting on Map keys
+    public void testOrderedMaps() throws Exception {
+        SortedKeysMap map = new SortedKeysMap()
+            .put("b", 2)
+            .put("a", 1);
+        assertEquals(aposToQuotes("{'values':{'a':1,'b':2}}"),
+                MAPPER.writeValueAsString(map));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/FormatFeatureUnwrapSingleTest.java b/src/test/java/com/fasterxml/jackson/databind/struct/FormatFeatureUnwrapSingleTest.java
new file mode 100644
index 0000000..ab8b38c
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/FormatFeatureUnwrapSingleTest.java
@@ -0,0 +1,192 @@
+package com.fasterxml.jackson.databind.struct;
+
+import java.net.URI;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+public class FormatFeatureUnwrapSingleTest extends BaseMapTest
+{
+    static class StringArrayNotAnnoted {
+        public String[] values;
+
+        protected StringArrayNotAnnoted() { }
+        public StringArrayNotAnnoted(String ... v) { values = v; }
+    }
+
+    @JsonPropertyOrder( { "strings", "ints", "bools" })
+    static class WrapWriteWithArrays
+    {
+        @JsonProperty("strings")
+        @JsonFormat(with={ JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED })
+        public String[] _strings = new String[] {
+            "a"
+        };
+
+        @JsonFormat(without={ JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED })
+        public int[] ints = new int[] { 1 };
+
+        public boolean[] bools = new boolean[] { true };
+    }
+
+    static class UnwrapShortArray {
+        @JsonFormat(with={ JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED })
+        public short[] v = { (short) 7 };
+    }
+
+    static class UnwrapIntArray {
+        @JsonFormat(with={ JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED })
+        public int[] v = { 3 };
+    }
+
+    static class UnwrapLongArray {
+        @JsonFormat(with={ JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED })
+        public long[] v = { 1L };
+    }
+
+    static class UnwrapBooleanArray {
+        @JsonFormat(with={ JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED })
+        public boolean[] v = { true };
+    }
+
+    static class UnwrapFloatArray {
+        @JsonFormat(with={ JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED })
+        public float[] v = { 0.5f };
+    }
+
+    static class UnwrapDoubleArray {
+        @JsonFormat(with={ JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED })
+        public double[] v = { 0.25 };
+    }
+
+    static class UnwrapIterable {
+        @JsonFormat(with={ JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED })
+        @JsonSerialize(as=Iterable.class)
+        public Iterable<String> v;
+
+        public UnwrapIterable() {
+            v = Collections.singletonList("foo");
+        }
+
+        public UnwrapIterable(String... values) {
+            v = Arrays.asList(values);
+        }
+    }
+
+    static class UnwrapCollection {
+        @JsonFormat(with={ JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED })
+        @JsonSerialize(as=Collection.class)
+        public Collection<String> v;
+
+        public UnwrapCollection() {
+            v = Collections.singletonList("foo");
+        }
+
+        public UnwrapCollection(String... values) {
+            v = new LinkedHashSet<String>(Arrays.asList(values));
+        }
+    }
+    
+    static class UnwrapStringLike {
+        @JsonFormat(with={ JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED })
+        public URI[] v = { URI.create("http://foo") };
+    }
+    
+    @JsonPropertyOrder( { "strings", "ints", "bools", "enums" })
+    static class WrapWriteWithCollections
+    {
+        @JsonFormat(with={ JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED })
+        public List<String> strings = Arrays.asList("a");
+
+        @JsonFormat(without={ JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED })
+        public Collection<Integer> ints = Arrays.asList(Integer.valueOf(1));
+
+        public Set<Boolean> bools = Collections.singleton(true);
+
+        @JsonFormat(with={ JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED })
+        public EnumSet<ABC> enums = EnumSet.of(ABC.B);
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods, writing with single-element unwrapping
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+
+    public void testWithArrayTypes() throws Exception
+    {
+        // default: strings unwrapped, ints wrapped
+        assertEquals(aposToQuotes("{'strings':'a','ints':[1],'bools':[true]}"),
+                MAPPER.writeValueAsString(new WrapWriteWithArrays()));
+
+        // change global default to "yes, unwrap"; changes 'bools' only
+        assertEquals(aposToQuotes("{'strings':'a','ints':[1],'bools':true}"),
+                MAPPER.writer().with(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)
+                .writeValueAsString(new WrapWriteWithArrays()));
+
+        // change global default to "no, don't, unwrap", same as first case
+        assertEquals(aposToQuotes("{'strings':'a','ints':[1],'bools':[true]}"),
+                MAPPER.writer().without(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)
+                .writeValueAsString(new WrapWriteWithArrays()));
+
+        // And then without SerializationFeature but with config override:
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.configOverride(String[].class).setFormat(JsonFormat.Value.empty()
+                .withFeature(JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED));
+        assertEquals(aposToQuotes("{'values':'a'}"),
+                mapper.writeValueAsString(new StringArrayNotAnnoted("a")));
+    }
+
+    public void testWithCollectionTypes() throws Exception
+    {
+        // default: strings unwrapped, ints wrapped
+        assertEquals(aposToQuotes("{'strings':'a','ints':[1],'bools':[true],'enums':'B'}"),
+                MAPPER.writeValueAsString(new WrapWriteWithCollections()));
+
+        // change global default to "yes, unwrap"; changes 'bools' only
+        assertEquals(aposToQuotes("{'strings':'a','ints':[1],'bools':true,'enums':'B'}"),
+                MAPPER.writer().with(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)
+                .writeValueAsString(new WrapWriteWithCollections()));
+
+        // change global default to "no, don't, unwrap", same as first case
+        assertEquals(aposToQuotes("{'strings':'a','ints':[1],'bools':[true],'enums':'B'}"),
+                MAPPER.writer().without(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)
+                .writeValueAsString(new WrapWriteWithCollections()));
+    }
+
+    public void testUnwrapWithPrimitiveArraysEtc() throws Exception {
+        assertEquals("{\"v\":7}", MAPPER.writeValueAsString(new UnwrapShortArray()));
+        assertEquals("{\"v\":3}", MAPPER.writeValueAsString(new UnwrapIntArray()));
+        assertEquals("{\"v\":1}", MAPPER.writeValueAsString(new UnwrapLongArray()));
+        assertEquals("{\"v\":true}", MAPPER.writeValueAsString(new UnwrapBooleanArray()));
+
+        assertEquals("{\"v\":0.5}", MAPPER.writeValueAsString(new UnwrapFloatArray()));
+        assertEquals("{\"v\":0.25}", MAPPER.writeValueAsString(new UnwrapDoubleArray()));
+        assertEquals("0.5",
+                MAPPER.writer().with(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)
+                .writeValueAsString(new double[] { 0.5 }));
+
+        assertEquals("{\"v\":\"foo\"}", MAPPER.writeValueAsString(new UnwrapIterable()));
+        assertEquals("{\"v\":\"x\"}", MAPPER.writeValueAsString(new UnwrapIterable("x")));
+        assertEquals("{\"v\":[\"x\",null]}", MAPPER.writeValueAsString(new UnwrapIterable("x", null)));
+
+        assertEquals("{\"v\":\"foo\"}", MAPPER.writeValueAsString(new UnwrapCollection()));
+        assertEquals("{\"v\":\"x\"}", MAPPER.writeValueAsString(new UnwrapCollection("x")));
+        assertEquals("{\"v\":[\"x\",null]}", MAPPER.writeValueAsString(new UnwrapCollection("x", null)));
+
+        assertEquals("{\"v\":\"http://foo\"}", MAPPER.writeValueAsString(new UnwrapStringLike()));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/FormatFeaturesTest.java b/src/test/java/com/fasterxml/jackson/databind/struct/FormatFeaturesTest.java
deleted file mode 100644
index 87d5922..0000000
--- a/src/test/java/com/fasterxml/jackson/databind/struct/FormatFeaturesTest.java
+++ /dev/null
@@ -1,254 +0,0 @@
-package com.fasterxml.jackson.databind.struct;
-
-import java.util.*;
-
-import com.fasterxml.jackson.annotation.*;
-import com.fasterxml.jackson.databind.*;
-
-/**
- * Tests for {@link JsonFormat} and specifically <code>JsonFormat.Feature</code>s.
- */
-public class FormatFeaturesTest extends BaseMapTest
-{
-    @JsonPropertyOrder( { "strings", "ints", "bools" })
-    static class WrapWriteWithArrays
-    {
-        @JsonProperty("strings")
-        @JsonFormat(with={ JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED })
-        public String[] _strings = new String[] {
-            "a"
-        };
-
-        @JsonFormat(without={ JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED })
-        public int[] ints = new int[] {
-            1
-        };
-
-        public boolean[] bools = new boolean[] { true };
-    }
-
-    @JsonPropertyOrder( { "strings", "ints", "bools", "enums" })
-    static class WrapWriteWithCollections
-    {
-        @JsonFormat(with={ JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED })
-        public List<String> strings = Arrays.asList("a");
-
-        @JsonFormat(without={ JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED })
-        public Collection<Integer> ints = Arrays.asList(Integer.valueOf(1));
-
-        public Set<Boolean> bools = Collections.singleton(true);
-
-        @JsonFormat(with={ JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED })
-        public EnumSet<ABC> enums = EnumSet.of(ABC.B);
-    }
-
-    static class StringArrayWrapper {
-        @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
-        public String[] values;
-    }
-
-    static class StringArrayNotAnnoted {
-        public String[] values;
-
-        protected StringArrayNotAnnoted() { }
-        public StringArrayNotAnnoted(String ... v) { values = v; }
-    }
-    
-    static class IntArrayWrapper {
-        @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
-        public int[] values;
-    }
-
-    static class LongArrayWrapper {
-        @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
-        public long[] values;
-    }
-
-    static class StringListWrapper {
-        @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
-        public List<String> values;
-    }
-
-    static class EnumSetWrapper {
-        @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
-        public EnumSet<ABC> values;
-    }
-    
-    static class RolesInArray {
-        @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
-        public Role[] roles;
-    }
-
-    static class RolesInList {
-        @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
-        public List<Role> roles;
-    }
-    
-    static class Role {
-        public String ID;
-        public String Name;
-    }
-
-    static class CaseInsensitiveRoleWrapper
-    {
-        @JsonFormat(with={ JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES })
-        public Role role;
-    }
-
-    static class SortedKeysMap {
-        @JsonFormat(with = JsonFormat.Feature.WRITE_SORTED_MAP_ENTRIES)
-        public Map<String,Integer> values = new LinkedHashMap<>();
-
-        protected SortedKeysMap() { }
-
-        public SortedKeysMap put(String key, int value) {
-            values.put(key, value);
-            return this;
-        }
-    }
-
-    /*
-    /**********************************************************
-    /* Test methods, writing with single-element unwrapping
-    /**********************************************************
-     */
-    
-    private final ObjectMapper MAPPER = new ObjectMapper();
-
-    public void testWithArrayTypes() throws Exception
-    {
-        // default: strings unwrapped, ints wrapped
-        assertEquals(aposToQuotes("{'strings':'a','ints':[1],'bools':[true]}"),
-                MAPPER.writeValueAsString(new WrapWriteWithArrays()));
-
-        // change global default to "yes, unwrap"; changes 'bools' only
-        assertEquals(aposToQuotes("{'strings':'a','ints':[1],'bools':true}"),
-                MAPPER.writer().with(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)
-                .writeValueAsString(new WrapWriteWithArrays()));
-
-        // change global default to "no, don't, unwrap", same as first case
-        assertEquals(aposToQuotes("{'strings':'a','ints':[1],'bools':[true]}"),
-                MAPPER.writer().without(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)
-                .writeValueAsString(new WrapWriteWithArrays()));
-
-        // And then without SerializationFeature but with config override:
-        ObjectMapper mapper = new ObjectMapper();
-        mapper.configOverride(String[].class).setFormat(JsonFormat.Value.empty()
-                .withFeature(JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED));
-        assertEquals(aposToQuotes("{'values':'a'}"),
-                mapper.writeValueAsString(new StringArrayNotAnnoted("a")));
-    }
-
-    public void testWithCollectionTypes() throws Exception
-    {
-        // default: strings unwrapped, ints wrapped
-        assertEquals(aposToQuotes("{'strings':'a','ints':[1],'bools':[true],'enums':'B'}"),
-                MAPPER.writeValueAsString(new WrapWriteWithCollections()));
-
-        // change global default to "yes, unwrap"; changes 'bools' only
-        assertEquals(aposToQuotes("{'strings':'a','ints':[1],'bools':true,'enums':'B'}"),
-                MAPPER.writer().with(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)
-                .writeValueAsString(new WrapWriteWithCollections()));
-
-        // change global default to "no, don't, unwrap", same as first case
-        assertEquals(aposToQuotes("{'strings':'a','ints':[1],'bools':[true],'enums':'B'}"),
-                MAPPER.writer().without(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)
-                .writeValueAsString(new WrapWriteWithCollections()));
-    }
-
-    /*
-    /**********************************************************
-    /* Test methods, reading with single-element unwrapping
-    /**********************************************************
-     */
-
-    public void testSingleStringArrayRead() throws Exception {
-        String json = aposToQuotes(
-                "{ 'values': 'first' }");
-        StringArrayWrapper result = MAPPER.readValue(json, StringArrayWrapper.class);
-        assertNotNull(result.values);
-        assertEquals(1, result.values.length);
-        assertEquals("first", result.values[0]);
-
-        // and then without annotation, but with global override
-        ObjectMapper mapper = new ObjectMapper();
-        mapper.configOverride(String[].class).setFormat(JsonFormat.Value.empty()
-                .withFeature(JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY));
-        StringArrayNotAnnoted result2 = mapper.readValue(json, StringArrayNotAnnoted.class);
-        assertNotNull(result2.values);
-        assertEquals(1, result2.values.length);
-        assertEquals("first", result2.values[0]);
-    }
-
-    public void testSingleIntArrayRead() throws Exception {
-        String json = aposToQuotes(
-                "{ 'values': 123 }");
-        IntArrayWrapper result = MAPPER.readValue(json, IntArrayWrapper.class);
-        assertNotNull(result.values);
-        assertEquals(1, result.values.length);
-        assertEquals(123, result.values[0]);
-    }
-
-    public void testSingleLongArrayRead() throws Exception {
-        String json = aposToQuotes(
-                "{ 'values': -205 }");
-        LongArrayWrapper result = MAPPER.readValue(json, LongArrayWrapper.class);
-        assertNotNull(result.values);
-        assertEquals(1, result.values.length);
-        assertEquals(-205L, result.values[0]);
-    }
-
-    public void testSingleElementArrayRead() throws Exception {
-        String json = aposToQuotes(
-                "{ 'roles': { 'Name': 'User', 'ID': '333' } }");
-        RolesInArray response = MAPPER.readValue(json, RolesInArray.class);
-        assertNotNull(response.roles);
-        assertEquals(1, response.roles.length);
-        assertEquals("333", response.roles[0].ID);
-    }
-    
-    public void testSingleStringListRead() throws Exception {
-        String json = aposToQuotes(
-                "{ 'values': 'first' }");
-        StringListWrapper result = MAPPER.readValue(json, StringListWrapper.class);
-        assertNotNull(result.values);
-        assertEquals(1, result.values.size());
-        assertEquals("first", result.values.get(0));
-    }
-
-    public void testSingleElementListRead() throws Exception {
-        String json = aposToQuotes(
-                "{ 'roles': { 'Name': 'User', 'ID': '333' } }");
-        RolesInList response = MAPPER.readValue(json, RolesInList.class);
-        assertNotNull(response.roles);
-        assertEquals(1, response.roles.size());
-        assertEquals("333", response.roles.get(0).ID);
-    }
-
-    public void testSingleEnumSetRead() throws Exception {
-        EnumSetWrapper result = MAPPER.readValue(aposToQuotes("{ 'values': 'B' }"),
-                EnumSetWrapper.class);
-        assertNotNull(result.values);
-        assertEquals(1, result.values.size());
-        assertEquals(ABC.B, result.values.iterator().next());
-    }
-
-    // [databind#1232]: allow per-property case-insensitivity
-    public void testCaseInsensitive() throws Exception {
-        CaseInsensitiveRoleWrapper w = MAPPER.readValue
-                (aposToQuotes("{'role':{'id':'12','name':'Foo'}}"),
-                        CaseInsensitiveRoleWrapper.class);
-        assertNotNull(w);
-        assertEquals("12", w.role.ID);
-        assertEquals("Foo", w.role.Name);
-    }
-
-    // [databind#1232]: allow forcing sorting on Map keys
-    public void testOrderedMaps() throws Exception {
-        SortedKeysMap map = new SortedKeysMap()
-            .put("b", 2)
-            .put("a", 1);
-        assertEquals(aposToQuotes("{'values':{'a':1,'b':2}}"),
-                MAPPER.writeValueAsString(map));
-    }
-}
diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/ScalarCoercionTest.java b/src/test/java/com/fasterxml/jackson/databind/struct/ScalarCoercionTest.java
new file mode 100644
index 0000000..e524d72
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/ScalarCoercionTest.java
@@ -0,0 +1,195 @@
+package com.fasterxml.jackson.databind.struct;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.exc.MismatchedInputException;
+
+// for [databind#1106]
+public class ScalarCoercionTest extends BaseMapTest
+{
+    private final ObjectMapper COERCING_MAPPER = new ObjectMapper(); {
+        COERCING_MAPPER.enable(MapperFeature.ALLOW_COERCION_OF_SCALARS);
+    }
+
+    private final ObjectMapper NOT_COERCING_MAPPER = new ObjectMapper(); {
+        NOT_COERCING_MAPPER.disable(MapperFeature.ALLOW_COERCION_OF_SCALARS);
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests: coercion from empty String
+    /**********************************************************
+     */
+
+    public void testNullValueFromEmpty() throws Exception
+    {
+        // wrappers accept `null` fine
+        _verifyNullOkFromEmpty(Boolean.class, null);
+        // but primitives require non-null
+        _verifyNullOkFromEmpty(Boolean.TYPE, Boolean.FALSE);
+
+        _verifyNullOkFromEmpty(Byte.class, null);
+        _verifyNullOkFromEmpty(Byte.TYPE, Byte.valueOf((byte) 0));
+        _verifyNullOkFromEmpty(Short.class, null);
+        _verifyNullOkFromEmpty(Short.TYPE, Short.valueOf((short) 0));
+        _verifyNullOkFromEmpty(Character.class, null);
+        _verifyNullOkFromEmpty(Character.TYPE, Character.valueOf((char) 0));
+        _verifyNullOkFromEmpty(Integer.class, null);
+        _verifyNullOkFromEmpty(Integer.TYPE, Integer.valueOf(0));
+        _verifyNullOkFromEmpty(Long.class, null);
+        _verifyNullOkFromEmpty(Long.TYPE, Long.valueOf(0L));
+        _verifyNullOkFromEmpty(Float.class, null);
+        _verifyNullOkFromEmpty(Float.TYPE, Float.valueOf(0.0f));
+        _verifyNullOkFromEmpty(Double.class, null);
+        _verifyNullOkFromEmpty(Double.TYPE, Double.valueOf(0.0));
+
+        _verifyNullOkFromEmpty(BigInteger.class, null);
+        _verifyNullOkFromEmpty(BigDecimal.class, null);
+    }
+
+    private void _verifyNullOkFromEmpty(Class<?> type, Object exp) throws IOException
+    {
+        Object result = COERCING_MAPPER.readerFor(type)
+                .with(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)
+                .readValue("\"\"");
+        if (exp == null) {
+            assertNull(result);
+        } else {
+            assertEquals(exp, result);
+        }
+    }
+
+    public void testNullFailFromEmpty() throws Exception
+    {
+        _verifyNullFail(Boolean.class);
+        _verifyNullFail(Boolean.TYPE);
+
+        _verifyNullFail(Byte.class);
+        _verifyNullFail(Byte.TYPE);
+        _verifyNullFail(Short.class);
+        _verifyNullFail(Short.TYPE);
+        _verifyNullFail(Character.class);
+        _verifyNullFail(Character.TYPE);
+        _verifyNullFail(Integer.class);
+        _verifyNullFail(Integer.TYPE);
+        _verifyNullFail(Long.class);
+        _verifyNullFail(Long.TYPE);
+        _verifyNullFail(Float.class);
+        _verifyNullFail(Float.TYPE);
+        _verifyNullFail(Double.class);
+        _verifyNullFail(Double.TYPE);
+
+        _verifyNullFail(BigInteger.class);
+        _verifyNullFail(BigDecimal.class);
+    }
+
+    private void _verifyNullFail(Class<?> type) throws IOException
+    {
+        try {
+            NOT_COERCING_MAPPER.readerFor(type).readValue("\"\"");
+            fail("Should have failed for "+type);
+        } catch (MismatchedInputException e) {
+            verifyException(e, "Cannot coerce empty String");
+            verifyException(e, "Null value for");
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests: coercion from secondary representations
+    /**********************************************************
+     */
+
+    public void testStringCoercionOk() throws Exception
+    {
+        // first successful coercions. Boolean has a ton...
+        _verifyCoerceSuccess("1", Boolean.TYPE, Boolean.TRUE);
+        _verifyCoerceSuccess("1", Boolean.class, Boolean.TRUE);
+        _verifyCoerceSuccess(quote("true"), Boolean.TYPE, Boolean.TRUE);
+        _verifyCoerceSuccess(quote("true"), Boolean.class, Boolean.TRUE);
+        _verifyCoerceSuccess(quote("True"), Boolean.TYPE, Boolean.TRUE);
+        _verifyCoerceSuccess(quote("True"), Boolean.class, Boolean.TRUE);
+        _verifyCoerceSuccess("0", Boolean.TYPE, Boolean.FALSE);
+        _verifyCoerceSuccess("0", Boolean.class, Boolean.FALSE);
+        _verifyCoerceSuccess(quote("false"), Boolean.TYPE, Boolean.FALSE);
+        _verifyCoerceSuccess(quote("false"), Boolean.class, Boolean.FALSE);
+        _verifyCoerceSuccess(quote("False"), Boolean.TYPE, Boolean.FALSE);
+        _verifyCoerceSuccess(quote("False"), Boolean.class, Boolean.FALSE);
+
+        _verifyCoerceSuccess(quote("123"), Byte.TYPE, Byte.valueOf((byte) 123));
+        _verifyCoerceSuccess(quote("123"), Byte.class, Byte.valueOf((byte) 123));
+        _verifyCoerceSuccess(quote("123"), Short.TYPE, Short.valueOf((short) 123));
+        _verifyCoerceSuccess(quote("123"), Short.class, Short.valueOf((short) 123));
+        _verifyCoerceSuccess(quote("123"), Integer.TYPE, Integer.valueOf(123));
+        _verifyCoerceSuccess(quote("123"), Integer.class, Integer.valueOf(123));
+        _verifyCoerceSuccess(quote("123"), Long.TYPE, Long.valueOf(123));
+        _verifyCoerceSuccess(quote("123"), Long.class, Long.valueOf(123));
+        _verifyCoerceSuccess(quote("123.5"), Float.TYPE, Float.valueOf(123.5f));
+        _verifyCoerceSuccess(quote("123.5"), Float.class, Float.valueOf(123.5f));
+        _verifyCoerceSuccess(quote("123.5"), Double.TYPE, Double.valueOf(123.5));
+        _verifyCoerceSuccess(quote("123.5"), Double.class, Double.valueOf(123.5));
+
+        _verifyCoerceSuccess(quote("123"), BigInteger.class, BigInteger.valueOf(123));
+        _verifyCoerceSuccess(quote("123.0"), BigDecimal.class, new BigDecimal("123.0"));
+    }
+
+    public void testStringCoercionFail() throws Exception
+    {
+        _verifyCoerceFail(quote("true"), Boolean.TYPE);
+        _verifyCoerceFail(quote("true"), Boolean.class);
+        _verifyCoerceFail(quote("123"), Byte.TYPE);
+        _verifyCoerceFail(quote("123"), Byte.class);
+        _verifyCoerceFail(quote("123"), Short.TYPE);
+        _verifyCoerceFail(quote("123"), Short.class);
+        _verifyCoerceFail(quote("123"), Integer.TYPE);
+        _verifyCoerceFail(quote("123"), Integer.class);
+        _verifyCoerceFail(quote("123"), Long.TYPE);
+        _verifyCoerceFail(quote("123"), Long.class);
+        _verifyCoerceFail(quote("123.5"), Float.TYPE);
+        _verifyCoerceFail(quote("123.5"), Float.class);
+        _verifyCoerceFail(quote("123.5"), Double.TYPE);
+        _verifyCoerceFail(quote("123.5"), Double.class);
+
+        _verifyCoerceFail(quote("123"), BigInteger.class);
+        _verifyCoerceFail(quote("123.0"), BigDecimal.class);
+    }
+
+    public void testMiscCoercionFail() throws Exception
+    {
+        // And then we have coercions from more esoteric types too
+        _verifyCoerceFail("1", Boolean.TYPE);
+        _verifyCoerceFail("1", Boolean.class);
+
+        _verifyCoerceFail("65", Character.class);
+        _verifyCoerceFail("65", Character.TYPE);
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+
+    private void _verifyCoerceSuccess(String input, Class<?> type, Object exp) throws IOException
+    {
+        Object result = COERCING_MAPPER.readerFor(type)
+                .readValue(input);
+        assertEquals(exp, result);
+    }
+
+    private void _verifyCoerceFail(String input, Class<?> type) throws IOException
+    {
+        try {
+            NOT_COERCING_MAPPER.readerFor(type)
+                .readValue(input);
+            fail("Should not have allowed coercion");
+        } catch (MismatchedInputException e) {
+            verifyException(e, "Cannot coerce ");
+            verifyException(e, " for type `");
+            verifyException(e, "enable `MapperFeature.ALLOW_COERCION_OF_SCALARS` to allow");
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/SingleValueAsArrayTest.java b/src/test/java/com/fasterxml/jackson/databind/struct/SingleValueAsArrayTest.java
index 1903b2e..f0f9354 100644
--- a/src/test/java/com/fasterxml/jackson/databind/struct/SingleValueAsArrayTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/SingleValueAsArrayTest.java
@@ -71,16 +71,33 @@
 
     public void testSuccessfulDeserializationOfObjectWithChainedArrayCreators() throws IOException
     {
-        MAPPER.readValue(JSON, Bean1421A.class);
+        Bean1421A result = MAPPER.readValue(JSON, Bean1421A.class);
+        assertNotNull(result);
     }
 
     public void testWithSingleString() throws Exception {
-        ObjectMapper objectMapper = new ObjectMapper();
-        objectMapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
-        Bean1421B<List<String>> a = objectMapper.readValue(quote("test2"),
+        Bean1421B<List<String>> a = MAPPER.readValue(quote("test2"),
                 new TypeReference<Bean1421B<List<String>>>() {});
         List<String> expected = new ArrayList<>();
         expected.add("test2");
         assertEquals(expected, a.value);
     }
+
+    public void testPrimitives() throws Exception {
+        int[] i = MAPPER.readValue("16", int[].class);
+        assertEquals(1, i.length);
+        assertEquals(16, i[0]);
+
+        long[] l = MAPPER.readValue("1234", long[].class);
+        assertEquals(1, l.length);
+        assertEquals(1234L, l[0]);
+
+        double[] d = MAPPER.readValue("12.5", double[].class);
+        assertEquals(1, d.length);
+        assertEquals(12.5, d[0]);
+
+        boolean[] b = MAPPER.readValue("true", boolean[].class);
+        assertEquals(1, d.length);
+        assertEquals(true, b[0]);
+    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/TestPOJOAsArray.java b/src/test/java/com/fasterxml/jackson/databind/struct/TestPOJOAsArray.java
index 7e8f091..391cd3c 100644
--- a/src/test/java/com/fasterxml/jackson/databind/struct/TestPOJOAsArray.java
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/TestPOJOAsArray.java
@@ -7,6 +7,7 @@
 import com.fasterxml.jackson.annotation.JsonFormat.Shape;
 
 import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.exc.MismatchedInputException;
 import com.fasterxml.jackson.databind.introspect.Annotated;
 import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
 
@@ -191,8 +192,7 @@
     /* Compatibility with "single-elem as array" feature
     /*****************************************************
      */
-    
-    // for [JACKSON-805]
+
     public void testSerializeAsArrayWithSingleProperty() throws Exception {
         ObjectMapper mapper = new ObjectMapper();
         mapper.enable(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED);
@@ -262,4 +262,32 @@
         assertNotNull(result);
         assertEquals(3, result.y);
     }
+
+    /*
+    /*****************************************************
+    /* Failure tests
+    /*****************************************************
+     */
+
+    public void testUnknownExtraProp() throws Exception
+    {
+        String json = "{\"value\":[true,\"Foobar\",42,13, false]}";
+        try {
+            MAPPER.readValue(json, PojoAsArrayWrapper.class);
+            fail("should not pass with extra element");
+        } catch (MismatchedInputException e) {
+            verifyException(e, "Unexpected JSON values");
+        }
+
+        // but actually fine if skip-unknown set
+        PojoAsArrayWrapper v = MAPPER.readerFor(PojoAsArrayWrapper.class)
+                .without(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
+                .readValue(json);
+        assertNotNull(v);
+        // note: +1 for both so
+        assertEquals(v.value.x, 42);
+        assertEquals(v.value.y, 13);
+        assertTrue(v.value.complete);
+        assertEquals("Foobar", v.value.name);
+    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/TestPOJOAsArrayAdvanced.java b/src/test/java/com/fasterxml/jackson/databind/struct/TestPOJOAsArrayAdvanced.java
index 989669c..d2e6ddd 100644
--- a/src/test/java/com/fasterxml/jackson/databind/struct/TestPOJOAsArrayAdvanced.java
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/TestPOJOAsArrayAdvanced.java
@@ -56,6 +56,27 @@
         public int c;
     }
 
+    @JsonFormat(shape=JsonFormat.Shape.ARRAY)
+    @JsonPropertyOrder(alphabetic=true)
+    static class AsArrayWithViewAndCreator
+    {
+        @JsonView(ViewA.class)
+        public int a;
+        @JsonView(ViewB.class)
+        public int b;
+        public int c;
+
+        @JsonCreator
+        public AsArrayWithViewAndCreator(@JsonProperty("a") int a,
+                @JsonProperty("b") int b,
+                @JsonProperty("c") int c)
+        {
+            this.a = a;
+            this.b = b;
+            this.c = c;
+        }
+    }
+
     /*
     /*****************************************************
     /* Basic tests
@@ -75,12 +96,23 @@
         assertEquals("[1,null,3]", json);
 
         // and then that conversely deserializer does something similar
-        AsArrayWithView output = MAPPER.readerFor(AsArrayWithView.class).withView(ViewB.class)
+        AsArrayWithView result = MAPPER.readerFor(AsArrayWithView.class).withView(ViewB.class)
                 .readValue("[1,2,3]");
         // should include 'c' (not view-able) and 'b' (include in ViewB) but not 'a'
-        assertEquals(3, output.c);
-        assertEquals(2, output.b);
-        assertEquals(0, output.a);
+        assertEquals(3, result.c);
+        assertEquals(2, result.b);
+        assertEquals(0, result.a);
+    }
+
+    public void testWithViewAndCreator() throws Exception
+    {
+        AsArrayWithViewAndCreator result = MAPPER.readerFor(AsArrayWithViewAndCreator.class)
+                .withView(ViewB.class)
+                .readValue("[1,2,3]");
+        // should include 'c' (not view-able) and 'b' (include in ViewB) but not 'a'
+        assertEquals(3, result.c);
+        assertEquals(2, result.b);
+        assertEquals(0, result.a);
     }
 
     public void testWithCreatorsOrdered() throws Exception
diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/TestPOJOAsArrayWithBuilder.java b/src/test/java/com/fasterxml/jackson/databind/struct/TestPOJOAsArrayWithBuilder.java
index 2f572a4..321c86c 100644
--- a/src/test/java/com/fasterxml/jackson/databind/struct/TestPOJOAsArrayWithBuilder.java
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/TestPOJOAsArrayWithBuilder.java
@@ -1,9 +1,14 @@
 package com.fasterxml.jackson.databind.struct;
 
 import com.fasterxml.jackson.annotation.*;
+
 import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.DeserializationFeature;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectReader;
 import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.exc.MismatchedInputException;
+import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
 
 /**
  * Unit tests for "POJO as array" feature using Builder-style
@@ -28,6 +33,12 @@
     static class SimpleBuilderXY
     {
         public int x, y;
+
+        protected SimpleBuilderXY() { }
+        protected SimpleBuilderXY(int x0, int y0) {
+            x = x0;
+            y = y0;
+        }
         
         public SimpleBuilderXY withX(int x0) {
             this.x = x0;
@@ -43,7 +54,48 @@
             return new ValueClassXY(x, y);
         }
     }
-    
+
+    // Also, with creator:
+
+    @JsonDeserialize(builder=CreatorBuilder.class)
+    @JsonFormat(shape=JsonFormat.Shape.ARRAY)
+    @JsonPropertyOrder(alphabetic=true)
+    static class CreatorValue
+    {
+        final int a, b, c;
+
+        protected CreatorValue(int a, int b, int c) {
+            this.a = a;
+            this.b = b;
+            this.c = c;
+        }
+    }
+
+    @JsonFormat(shape=JsonFormat.Shape.ARRAY)
+    static class CreatorBuilder {
+        private final int a, b;
+
+        private int c;
+
+        @JsonCreator
+        public CreatorBuilder(@JsonProperty("a") int a,
+                @JsonProperty("b") int b)
+        {
+            this.a = a;
+            this.b = b;
+        }
+        
+        @JsonView(String.class)
+        public CreatorBuilder withC(int v) {
+            c = v;
+            return this;
+        }
+
+        public CreatorValue build() {
+            return new CreatorValue(a, b, c);
+        }
+    }
+
     /*
     /*****************************************************
     /* Basic tests
@@ -59,4 +111,95 @@
         assertEquals(2, value._x);
         assertEquals(3, value._y);
     }
+
+    // Won't work, but verify exception
+    public void testBuilderWithUpdate() throws Exception
+    {
+        // Ok, first, simple case of all values being present
+        try {
+            /*value =*/ MAPPER.readerFor(ValueClassXY.class)
+                    .withValueToUpdate(new ValueClassXY(6, 7))
+                    .readValue("[1,2]");
+            fail("Should not pass");
+        } catch (InvalidDefinitionException e) {
+            verifyException(e, "Deserialization of");
+            verifyException(e, "by passing existing instance");
+            verifyException(e, "ValueClassXY");
+        }
+    }
+
+    /*
+    /*****************************************************
+    /* Creator test(s)
+    /*****************************************************
+     */
+    
+    // test to ensure @JsonCreator also works
+    public void testWithCreator() throws Exception
+    {
+        CreatorValue value = MAPPER.readValue("[1,2,3]", CreatorValue.class);
+        assertEquals(1, value.a);
+        assertEquals(2, value.b);
+        assertEquals(3, value.c);
+
+        // and should be ok with partial too?
+        value = MAPPER.readValue("[1,2]", CreatorValue.class);
+        assertEquals(1, value.a);
+        assertEquals(2, value.b);
+        assertEquals(0, value.c);
+
+        value = MAPPER.readValue("[1]", CreatorValue.class);
+        assertEquals(1, value.a);
+        assertEquals(0, value.b);
+        assertEquals(0, value.c);
+
+        value = MAPPER.readValue("[]", CreatorValue.class);
+        assertEquals(0, value.a);
+        assertEquals(0, value.b);
+        assertEquals(0, value.c);
+    }
+
+    public void testWithCreatorAndView() throws Exception
+    {
+        ObjectReader reader = MAPPER.readerFor(CreatorValue.class);
+        CreatorValue value;
+
+        // First including values in view
+        value = reader.withView(String.class).readValue("[1,2,3]");
+        assertEquals(1, value.a);
+        assertEquals(2, value.b);
+        assertEquals(3, value.c);
+
+        // then not including view
+        value = reader.withView(Character.class).readValue("[1,2,3]");
+        assertEquals(1, value.a);
+        assertEquals(2, value.b);
+        assertEquals(0, value.c);
+    }
+
+    /*
+    /*****************************************************
+    /* Failure tests
+    /*****************************************************
+     */
+
+    public void testUnknownExtraProp() throws Exception
+    {
+        String json = "[1, 2, 3, 4]";
+        try {
+            MAPPER.readValue(json, ValueClassXY.class);
+            fail("should not pass with extra element");
+        } catch (MismatchedInputException e) {
+            verifyException(e, "Unexpected JSON values");
+        }
+
+        // but actually fine if skip-unknown set
+        ValueClassXY v = MAPPER.readerFor(ValueClassXY.class)
+                .without(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
+                .readValue(json);
+        assertNotNull(v);
+        // note: +1 for both so
+        assertEquals(v._x, 2);
+        assertEquals(v._y, 3);
+    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/TestUnwrapped.java b/src/test/java/com/fasterxml/jackson/databind/struct/TestUnwrapped.java
index dd373f6..d6536f8 100644
--- a/src/test/java/com/fasterxml/jackson/databind/struct/TestUnwrapped.java
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/TestUnwrapped.java
@@ -23,6 +23,17 @@
         }
     }
 
+    final static class Location {
+        public int x;
+        public int y;
+
+        public Location() { }
+        public Location(int x, int y) {
+            this.x = x;
+            this.y = y;
+        }
+    }
+    
     static class DeepUnwrapping
     {
         @JsonUnwrapped
@@ -45,17 +56,6 @@
             name = n;
         }
     }
-    
-    final static class Location {
-        public int x;
-        public int y;
-
-        public Location() { }
-        public Location(int x, int y) {
-            this.x = x;
-            this.y = y;
-        }
-    }
 
     // Class with two unwrapped properties
     static class TwoUnwrappedProperties {
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestUnwrappedIssue383.java b/src/test/java/com/fasterxml/jackson/databind/struct/TestUnwrappedRecursive383.java
similarity index 82%
rename from src/test/java/com/fasterxml/jackson/failing/TestUnwrappedIssue383.java
rename to src/test/java/com/fasterxml/jackson/databind/struct/TestUnwrappedRecursive383.java
index a9a8746..5b41be6 100644
--- a/src/test/java/com/fasterxml/jackson/failing/TestUnwrappedIssue383.java
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/TestUnwrappedRecursive383.java
@@ -1,12 +1,13 @@
-package com.fasterxml.jackson.failing;
+package com.fasterxml.jackson.databind.struct;
 
 import com.fasterxml.jackson.annotation.*;
 
 import com.fasterxml.jackson.databind.*;
 
-public class TestUnwrappedIssue383 extends BaseMapTest
+// Problem with recursive definition of unwrapping
+public class TestUnwrappedRecursive383 extends BaseMapTest
 {
-    // [Issue#383]
+    // [databind#383]
     static class RecursivePerson {
         public String name;
         public int age;
diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/TestUnwrappedWithPrefix.java b/src/test/java/com/fasterxml/jackson/databind/struct/TestUnwrappedWithPrefix.java
index 5d439c5..29e742f 100644
--- a/src/test/java/com/fasterxml/jackson/databind/struct/TestUnwrappedWithPrefix.java
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/TestUnwrappedWithPrefix.java
@@ -137,10 +137,6 @@
     static class SubChild {
         public String value;
     }
-    
-    // // // Reuse mapper to keep tests bit faster
-
-    private final ObjectMapper MAPPER = new ObjectMapper();
 
     /*
     /**********************************************************
@@ -148,6 +144,8 @@
     /**********************************************************
      */
 
+    private final ObjectMapper MAPPER = new ObjectMapper();
+
     public void testPrefixedUnwrappingSerialize() throws Exception
     {
         assertEquals("{\"name\":\"Tatu\",\"_x\":1,\"_y\":2}",
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestUnwrappedWithTypeInfo.java b/src/test/java/com/fasterxml/jackson/databind/struct/TestUnwrappedWithTypeInfo.java
similarity index 97%
rename from src/test/java/com/fasterxml/jackson/databind/ser/TestUnwrappedWithTypeInfo.java
rename to src/test/java/com/fasterxml/jackson/databind/struct/TestUnwrappedWithTypeInfo.java
index d199044..5a5f2cc 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/TestUnwrappedWithTypeInfo.java
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/TestUnwrappedWithTypeInfo.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.databind.ser;
+package com.fasterxml.jackson.databind.struct;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/UnwrapSingleArrayScalarsTest.java b/src/test/java/com/fasterxml/jackson/databind/struct/UnwrapSingleArrayScalarsTest.java
new file mode 100644
index 0000000..4352835
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/UnwrapSingleArrayScalarsTest.java
@@ -0,0 +1,408 @@
+package com.fasterxml.jackson.databind.struct;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.URI;
+import java.util.UUID;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectReader;
+import com.fasterxml.jackson.databind.exc.MismatchedInputException;
+
+public class UnwrapSingleArrayScalarsTest extends BaseMapTest
+{
+    static class BooleanBean {
+        boolean _v;
+        void setV(boolean v) { _v = v; }
+    }
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+
+    private final ObjectReader NO_UNWRAPPING_READER = MAPPER.reader();
+    private final ObjectReader UNWRAPPING_READER = MAPPER.reader()
+            .with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
+
+    /*
+    /**********************************************************
+    /* Tests for boolean
+    /**********************************************************
+     */
+    
+    public void testBooleanPrimitiveArrayUnwrap() throws Exception
+    {
+        // [databind#381]
+        final ObjectMapper mapper = new ObjectMapper();
+        mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
+        BooleanBean result = mapper.readValue(new StringReader("{\"v\":[true]}"), BooleanBean.class);
+        assertTrue(result._v);
+
+        _verifyMultiValueArrayFail("[{\"v\":[true,true]}]", BooleanBean.class);
+
+        result = mapper.readValue("{\"v\":[null]}", BooleanBean.class);
+        assertNotNull(result);
+        assertFalse(result._v);
+        
+        result = mapper.readValue("[{\"v\":[null]}]", BooleanBean.class);
+        assertNotNull(result);
+        assertFalse(result._v);
+        
+        boolean[] array = mapper.readValue(new StringReader("[ [ null ] ]"), boolean[].class);
+        assertNotNull(array);
+        assertEquals(1, array.length);
+        assertFalse(array[0]);
+    }
+
+    /*
+    /**********************************************************
+    /* Single-element as array tests, numbers
+    /**********************************************************
+     */
+    
+    // [databind#381]
+    public void testSingleElementScalarArrays() throws Exception {
+        final int intTest = 932832;
+        final double doubleTest = 32.3234;
+        final long longTest = 2374237428374293423L;
+        final short shortTest = (short) intTest;
+        final float floatTest = 84.3743f;
+        final byte byteTest = (byte) 43;
+        final char charTest = 'c';
+
+        final ObjectMapper mapper = new ObjectMapper();
+        mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
+
+        final int intValue = mapper.readValue(asArray(intTest), Integer.TYPE);
+        assertEquals(intTest, intValue);
+        final Integer integerWrapperValue = mapper.readValue(asArray(Integer.valueOf(intTest)), Integer.class);
+        assertEquals(Integer.valueOf(intTest), integerWrapperValue);
+
+        final double doubleValue = mapper.readValue(asArray(doubleTest), Double.class);
+        assertEquals(doubleTest, doubleValue);
+        final Double doubleWrapperValue = mapper.readValue(asArray(Double.valueOf(doubleTest)), Double.class);
+        assertEquals(Double.valueOf(doubleTest), doubleWrapperValue);
+
+        final long longValue = mapper.readValue(asArray(longTest), Long.TYPE);
+        assertEquals(longTest, longValue);
+        final Long longWrapperValue = mapper.readValue(asArray(Long.valueOf(longTest)), Long.class);
+        assertEquals(Long.valueOf(longTest), longWrapperValue);
+
+        final short shortValue = mapper.readValue(asArray(shortTest), Short.TYPE);
+        assertEquals(shortTest, shortValue);
+        final Short shortWrapperValue = mapper.readValue(asArray(Short.valueOf(shortTest)), Short.class);
+        assertEquals(Short.valueOf(shortTest), shortWrapperValue);
+
+        final float floatValue = mapper.readValue(asArray(floatTest), Float.TYPE);
+        assertEquals(floatTest, floatValue);
+        final Float floatWrapperValue = mapper.readValue(asArray(Float.valueOf(floatTest)), Float.class);
+        assertEquals(Float.valueOf(floatTest), floatWrapperValue);
+
+        final byte byteValue = mapper.readValue(asArray(byteTest), Byte.TYPE);
+        assertEquals(byteTest, byteValue);
+        final Byte byteWrapperValue = mapper.readValue(asArray(Byte.valueOf(byteTest)), Byte.class);
+        assertEquals(Byte.valueOf(byteTest), byteWrapperValue);
+
+        final char charValue = mapper.readValue(asArray(quote(String.valueOf(charTest))), Character.TYPE);
+        assertEquals(charTest, charValue);
+        final Character charWrapperValue = mapper.readValue(asArray(quote(String.valueOf(charTest))), Character.class);
+        assertEquals(Character.valueOf(charTest), charWrapperValue);
+
+        final boolean booleanTrueValue = mapper.readValue(asArray(true), Boolean.TYPE);
+        assertTrue(booleanTrueValue);
+
+        final boolean booleanFalseValue = mapper.readValue(asArray(false), Boolean.TYPE);
+        assertFalse(booleanFalseValue);
+
+        final Boolean booleanWrapperTrueValue = mapper.readValue(asArray(Boolean.valueOf(true)), Boolean.class);
+        assertEquals(Boolean.TRUE, booleanWrapperTrueValue);
+    }
+
+    public void testSingleElementArrayDisabled() throws Exception {
+        final ObjectMapper mapper = new ObjectMapper();
+        mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
+        try {
+            mapper.readValue("[42]", Integer.class);
+            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+        } catch (MismatchedInputException exp) {
+            //Exception was thrown correctly
+        }
+        try {
+            mapper.readValue("[42]", Integer.TYPE);
+            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+        } catch (MismatchedInputException exp) {
+            //Exception was thrown correctly
+        }
+        try {
+            mapper.readValue("[42342342342342]", Long.class);
+            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+        } catch (MismatchedInputException exp) {
+            //Exception was thrown correctly
+        }
+        try {
+            mapper.readValue("[42342342342342342]", Long.TYPE);
+            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+        } catch (MismatchedInputException exp) {
+            //Exception was thrown correctly
+        }
+
+        try {
+            mapper.readValue("[42]", Short.class);
+            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+        } catch (MismatchedInputException exp) {
+            //Exception was thrown correctly
+        }
+        try {
+            mapper.readValue("[42]", Short.TYPE);
+            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+        } catch (MismatchedInputException exp) {
+            //Exception was thrown correctly
+        }
+
+        try {
+            mapper.readValue("[327.2323]", Float.class);
+            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+        } catch (MismatchedInputException exp) {
+            //Exception was thrown correctly
+        }
+        try {
+            mapper.readValue("[82.81902]", Float.TYPE);
+            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+        } catch (MismatchedInputException exp) {
+            //Exception was thrown correctly
+        }
+
+        try {
+            mapper.readValue("[22]", Byte.class);
+            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+        } catch (MismatchedInputException exp) {
+            //Exception was thrown correctly
+        }
+        try {
+            mapper.readValue("[22]", Byte.TYPE);
+            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+        } catch (MismatchedInputException exp) {
+            //Exception was thrown correctly
+        }
+
+        try {
+            mapper.readValue("['d']", Character.class);
+            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+        } catch (MismatchedInputException exp) {
+            //Exception was thrown correctly
+        }
+        try {
+            mapper.readValue("['d']", Character.TYPE);
+            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+        } catch (MismatchedInputException exp) {
+            //Exception was thrown correctly
+        }
+
+        try {
+            mapper.readValue("[true]", Boolean.class);
+            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+        } catch (MismatchedInputException exp) {
+            //Exception was thrown correctly
+        }
+        try {
+            mapper.readValue("[true]", Boolean.TYPE);
+            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+        } catch (MismatchedInputException exp) {
+            //Exception was thrown correctly
+        }
+    }
+
+    public void testMultiValueArrayException() throws IOException {
+        _verifyMultiValueArrayFail("[42,42]", Integer.class);
+        _verifyMultiValueArrayFail("[42,42]", Integer.TYPE);
+        _verifyMultiValueArrayFail("[42342342342342,42342342342342]", Long.class);
+        _verifyMultiValueArrayFail("[42342342342342342,42342342342342]", Long.TYPE);
+        _verifyMultiValueArrayFail("[42,42]", Short.class);
+        _verifyMultiValueArrayFail("[42,42]", Short.TYPE);
+        _verifyMultiValueArrayFail("[22,23]", Byte.class);
+        _verifyMultiValueArrayFail("[22,23]", Byte.TYPE);
+        _verifyMultiValueArrayFail("[327.2323,327.2323]", Float.class);
+        _verifyMultiValueArrayFail("[82.81902,327.2323]", Float.TYPE);
+        _verifyMultiValueArrayFail("[42.273,42.273]", Double.class);
+        _verifyMultiValueArrayFail("[42.2723,42.273]", Double.TYPE);
+        _verifyMultiValueArrayFail(asArray(quote("c") + ","  + quote("d")), Character.class);
+        _verifyMultiValueArrayFail(asArray(quote("c") + ","  + quote("d")), Character.TYPE);
+        _verifyMultiValueArrayFail("[true,false]", Boolean.class);
+        _verifyMultiValueArrayFail("[true,false]", Boolean.TYPE);
+    }
+
+    /*
+    /**********************************************************
+    /* Simple non-primitive types
+    /**********************************************************
+     */
+
+    public void testSingleString() throws Exception
+    {
+        String value = "FOO!";
+        String result = MAPPER.readValue("\""+value+"\"", String.class);
+        assertEquals(value, result);
+    }
+    
+    public void testSingleStringWrapped() throws Exception
+    {
+        final ObjectMapper mapper = new ObjectMapper();
+        mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
+        
+        String value = "FOO!";
+        try {
+            mapper.readValue("[\""+value+"\"]", String.class);
+            fail("Exception not thrown when attempting to unwrap a single value 'String' array into a simple String");
+        } catch (MismatchedInputException exp) {
+            verifyException(exp, "Cannot deserialize");
+            verifyException(exp, "out of START_ARRAY");
+        }
+        
+        mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
+        
+        try {
+            mapper.readValue("[\""+value+"\",\""+value+"\"]", String.class);
+            fail("Exception not thrown when attempting to unwrap a single value 'String' array that contained more than one value into a simple String");
+        } catch (MismatchedInputException exp) {
+            verifyException(exp, "Attempted to unwrap");
+        }
+        String result = mapper.readValue("[\""+value+"\"]", String.class);
+        assertEquals(value, result);
+    }
+
+    public void testBigDecimal() throws Exception
+    {
+        final ObjectMapper mapper = objectMapper();
+        mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
+        
+        BigDecimal value = new BigDecimal("0.001");
+        BigDecimal result = mapper.readValue(value.toString(), BigDecimal.class);
+        assertEquals(value, result);
+        try {
+            mapper.readValue("[" + value.toString() + "]", BigDecimal.class);
+            fail("Exception was not thrown when attempting to read a single value array of BigDecimal when UNWRAP_SINGLE_VALUE_ARRAYS feature is disabled");
+        } catch (MismatchedInputException exp) {
+            verifyException(exp, "Cannot deserialize");
+            verifyException(exp, "out of START_ARRAY");
+        }
+        
+        mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
+        result = mapper.readValue("[" + value.toString() + "]", BigDecimal.class);
+        assertEquals(value, result);
+        
+        try {
+            mapper.readValue("[" + value.toString() + "," + value.toString() + "]", BigDecimal.class);
+            fail("Exception was not thrown when attempting to read a muti value array of BigDecimal when UNWRAP_SINGLE_VALUE_ARRAYS feature is enabled");
+        } catch (MismatchedInputException exp) {
+            verifyException(exp, "Attempted to unwrap");
+        }
+    }
+
+    public void testBigInteger() throws Exception
+    {
+        final ObjectMapper mapper = objectMapper();
+        mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
+        
+        BigInteger value = new BigInteger("-1234567890123456789012345567809");
+        BigInteger result = mapper.readValue(value.toString(), BigInteger.class);
+        assertEquals(value, result);
+
+        try {
+            mapper.readValue("[" + value.toString() + "]", BigInteger.class);
+            fail("Exception was not thrown when attempting to read a single value array of BigInteger when UNWRAP_SINGLE_VALUE_ARRAYS feature is disabled");
+        } catch (MismatchedInputException exp) {
+            verifyException(exp, "Cannot deserialize");
+            verifyException(exp, "out of START_ARRAY");
+        }
+        
+        mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
+        result = mapper.readValue("[" + value.toString() + "]", BigInteger.class);
+        assertEquals(value, result);
+        
+        try {
+            mapper.readValue("[" + value.toString() + "," + value.toString() + "]", BigInteger.class);
+            fail("Exception was not thrown when attempting to read a multi-value array of BigInteger when UNWRAP_SINGLE_VALUE_ARRAYS feature is enabled");
+        } catch (MismatchedInputException exp) {
+            verifyException(exp, "Attempted to unwrap");
+        }        
+    }
+
+    public void testClassAsArray() throws Exception
+    {
+        Class<?> result = MAPPER
+                    .readerFor(Class.class)
+                    .with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)
+                    .readValue(quote(String.class.getName()));
+        assertEquals(String.class, result);
+
+        try {
+            MAPPER.readerFor(Class.class)
+                .without(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)
+                .readValue("[" + quote(String.class.getName()) + "]");
+            fail("Did not throw exception when UNWRAP_SINGLE_VALUE_ARRAYS feature was disabled and attempted to read a Class array containing one element");
+        } catch (MismatchedInputException e) {
+            verifyException(e, "out of START_ARRAY token");
+        }
+
+        _verifyMultiValueArrayFail("[" + quote(Object.class.getName()) + "," + quote(Object.class.getName()) +"]",
+                Class.class);
+        result = MAPPER.readerFor(Class.class)
+                .with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)
+                .readValue("[" + quote(String.class.getName()) + "]");
+        assertEquals(String.class, result);
+    }
+
+    public void testURIAsArray() throws Exception
+    {
+        final ObjectReader reader = MAPPER.readerFor(URI.class);
+        final URI value = new URI("http://foo.com");
+        try {
+            reader.without(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)
+                .readValue("[\""+value.toString()+"\"]");
+            fail("Did not throw exception for single value array when UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+        } catch (MismatchedInputException e) {
+            verifyException(e, "out of START_ARRAY token");
+        }
+        
+        _verifyMultiValueArrayFail("[\""+value.toString()+"\",\""+value.toString()+"\"]", URI.class);
+    }
+
+    public void testUUIDAsArray() throws Exception
+    {
+        final ObjectReader reader = MAPPER.readerFor(UUID.class);
+        final String uuidStr = "76e6d183-5f68-4afa-b94a-922c1fdb83f8";
+        UUID uuid = UUID.fromString(uuidStr);
+        try {
+            NO_UNWRAPPING_READER.forType(UUID.class)
+                .readValue("[" + quote(uuidStr) + "]");
+            fail("Exception was not thrown when UNWRAP_SINGLE_VALUE_ARRAYS is disabled and attempted to read a single value array as a single element");
+        } catch (MismatchedInputException e) {
+            verifyException(e, "out of START_ARRAY token");
+        }
+        assertEquals(uuid,
+                reader.with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)
+                    .readValue("[" + quote(uuidStr) + "]"));
+        _verifyMultiValueArrayFail("[" + quote(uuidStr) + "," + quote(uuidStr) + "]", UUID.class);
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+
+    private void _verifyMultiValueArrayFail(String input, Class<?> type) throws IOException {
+        try {
+            UNWRAPPING_READER.forType(type).readValue(input);
+            fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled");
+        } catch (MismatchedInputException e) {
+            verifyException(e, "Attempted to unwrap");
+        }
+    }
+
+    private static String asArray(Object value) {
+        return "["+value+"]";
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/UnwrappedCreatorParam265Test.java b/src/test/java/com/fasterxml/jackson/databind/struct/UnwrappedCreatorParam265Test.java
new file mode 100644
index 0000000..62010d8
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/UnwrappedCreatorParam265Test.java
@@ -0,0 +1,101 @@
+package com.fasterxml.jackson.databind.struct;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
+
+public class UnwrappedCreatorParam265Test extends BaseMapTest
+{
+    static class JAddress {
+        public String address;
+        public String city;
+        public String state;
+
+        protected JAddress() { }
+
+        public JAddress(String address, String city, String state) {
+            this.address = address;
+            this.city = city;
+            this.state = state;
+        }
+    }
+
+    static class JPersonWithoutName
+    {
+        public String name;
+
+        protected JAddress _address;
+         
+        @JsonCreator
+        public JPersonWithoutName(@JsonProperty("name") String name,
+                @JsonUnwrapped JAddress address)
+        {
+            this.name = name;
+            _address = address;
+        }
+
+        @JsonUnwrapped
+        public JAddress getAddress() { return _address; }
+    }
+
+    static class JPersonWithName
+    {
+        public String name;
+
+        protected JAddress _address;
+         
+        @JsonCreator
+        public JPersonWithName(@JsonProperty("name") String name,
+                @JsonUnwrapped
+                @JsonProperty("address")
+                JAddress address)
+        {
+            this.name = name;
+            _address = address;
+        }
+
+        @JsonUnwrapped
+        public JAddress getAddress() { return _address; }
+    }
+    
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    // For [databind#265]: handle problem by throwing exception
+    public void testUnwrappedWithUnnamedCreatorParam() throws Exception
+    {
+        JPersonWithoutName person = new JPersonWithoutName("MyName", new JAddress("main street", "springfield", "WA"));
+        ObjectMapper mapper = new ObjectMapper();
+        // serialization should be fine as far as that goes
+        String json = mapper.writeValueAsString(person);
+
+        // but not deserialization:
+        try {
+            /*JPersonWithoutName result =*/ mapper.readValue(json, JPersonWithoutName.class);
+            fail("Should not pass");
+        } catch (InvalidDefinitionException e) {
+            verifyException(e, "Cannot define Creator parameter");
+            verifyException(e, "@JsonUnwrapped");
+        }
+    }
+
+    // For [databind#265]: handle problem by throwing exception
+    public void testUnwrappedWithNamedCreatorParam() throws Exception
+    {
+        JPersonWithName person = new JPersonWithName("MyName", new JAddress("main street", "springfield", "WA"));
+        ObjectMapper mapper = new ObjectMapper();
+        // serialization should be fine as far as that goes
+        String json = mapper.writeValueAsString(person);
+        try {
+            /*JPersonWithName result =*/ mapper.readValue(json, JPersonWithName.class);
+            fail("Should not pass");
+        } catch (InvalidDefinitionException e) {
+            verifyException(e, "Cannot define Creator property \"address\"");
+            verifyException(e, "@JsonUnwrapped");
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/UnwrappedWithView1559Test.java b/src/test/java/com/fasterxml/jackson/databind/struct/UnwrappedWithView1559Test.java
new file mode 100644
index 0000000..b23cf71
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/UnwrappedWithView1559Test.java
@@ -0,0 +1,36 @@
+package com.fasterxml.jackson.databind.struct;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonUnwrapped;
+import com.fasterxml.jackson.databind.*;
+
+public class UnwrappedWithView1559Test extends BaseMapTest
+{
+    @JsonInclude(JsonInclude.Include.NON_EMPTY)
+    static final class Health {
+        @JsonUnwrapped(prefix="xxx.")
+        public Status status;
+    }
+
+    // NOTE: `final` is required to trigger [databind#1559]
+    static final class Status {
+        public String code;
+    }
+
+    /*
+    /**********************************************************
+    /* Tests methods
+    /**********************************************************
+     */
+
+    // for [databind#1559]
+    public void testCanSerializeSimpleWithDefaultView() throws Exception {
+        String json = new ObjectMapper().configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false)
+                .writeValueAsString(new Health());
+        assertEquals(aposToQuotes("{}"), json);
+        // and just in case this, although won't matter wrt output
+        json = new ObjectMapper().configure(MapperFeature.DEFAULT_VIEW_INCLUSION, true)
+                .writeValueAsString(new Health());
+        assertEquals(aposToQuotes("{}"), json);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/type/ContainerTypesTest.java b/src/test/java/com/fasterxml/jackson/databind/type/ContainerTypesTest.java
new file mode 100644
index 0000000..e91f827
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/type/ContainerTypesTest.java
@@ -0,0 +1,113 @@
+package com.fasterxml.jackson.databind.type;
+
+import java.util.*;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.type.CollectionType;
+import com.fasterxml.jackson.databind.type.MapType;
+import com.fasterxml.jackson.databind.util.LRUMap;
+
+// for [databind#1415]
+public class ContainerTypesTest extends BaseMapTest
+{
+    static abstract class LongList implements List<Long> { }
+
+    static abstract class StringLongMap implements Map<String,Long> { }
+
+    /*
+    /**********************************************************
+    /* Unit tests, success
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+
+    public void testExplicitCollectionType() throws Exception
+    {
+        JavaType t = MAPPER.getTypeFactory()
+                .constructCollectionType(LongList.class, Long.class);
+        assertEquals(LongList.class, t.getRawClass());
+        assertEquals(Long.class, t.getContentType().getRawClass());
+    }
+
+    public void testImplicitCollectionType() throws Exception
+    {
+        JavaType t = MAPPER.getTypeFactory()
+                .constructParametricType(List.class, Long.class);
+        assertEquals(CollectionType.class, t.getClass());
+        assertEquals(List.class, t.getRawClass());
+        assertEquals(Long.class, t.getContentType().getRawClass());
+    }
+
+    // [databind#1725]
+    public void testMissingCollectionType() throws Exception
+    {
+        TypeFactory tf = MAPPER.getTypeFactory().withCache(new LRUMap<Object,JavaType>(4, 8));
+        JavaType t = tf.constructParametricType(List.class, HashMap.class);
+        assertEquals(CollectionType.class, t.getClass());
+        assertEquals(List.class, t.getRawClass());
+        assertEquals(HashMap.class, t.getContentType().getRawClass());
+    }
+
+    public void testExplicitMapType() throws Exception
+    {
+        JavaType t = MAPPER.getTypeFactory()
+                .constructMapType(StringLongMap.class,
+                        String.class, Long.class);
+        assertEquals(StringLongMap.class, t.getRawClass());
+        assertEquals(String.class, t.getKeyType().getRawClass());
+        assertEquals(Long.class, t.getContentType().getRawClass());
+    }
+
+    public void testImplicitMapType() throws Exception
+    {
+        JavaType t = MAPPER.getTypeFactory()
+                .constructParametricType(Map.class, Long.class, Boolean.class);
+        assertEquals(MapType.class, t.getClass());
+        assertEquals(Long.class, t.getKeyType().getRawClass());
+        assertEquals(Boolean.class, t.getContentType().getRawClass());
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests, fails
+    /**********************************************************
+     */
+
+    public void testMismatchedCollectionType() throws Exception
+    {
+        try {
+            MAPPER.getTypeFactory()
+                .constructCollectionType(LongList.class, String.class);
+            fail("Should not pass");
+        } catch (IllegalArgumentException e) {
+            verifyException(e, "`"+getClass().getName()+"$LongList` did not resolve to something");
+            verifyException(e, "element type");
+        }
+    }
+
+    public void testMismatchedMapType() throws Exception
+    {
+        // first, mismatched key type
+        try {
+            MAPPER.getTypeFactory()
+                .constructMapType(StringLongMap.class, Boolean.class, Long.class);
+            fail("Should not pass");
+        } catch (IllegalArgumentException e) {
+            verifyException(e, "`"+getClass().getName()+"$StringLongMap` did not resolve to something");
+            verifyException(e, "key type");
+        }
+        // then, mismatched value type
+        try {
+            MAPPER.getTypeFactory()
+                .constructMapType(StringLongMap.class, String.class, Class.class);
+            fail("Should not pass");
+        } catch (IllegalArgumentException e) {
+            verifyException(e, "`"+getClass().getName()+"$StringLongMap` did not resolve to something");
+            verifyException(e, "value type");
+        }
+    }
+}
+
diff --git a/src/test/java/com/fasterxml/jackson/databind/type/NestedTypes1604Test.java b/src/test/java/com/fasterxml/jackson/databind/type/NestedTypes1604Test.java
index 753f017..bf77b68 100644
--- a/src/test/java/com/fasterxml/jackson/databind/type/NestedTypes1604Test.java
+++ b/src/test/java/com/fasterxml/jackson/databind/type/NestedTypes1604Test.java
@@ -89,7 +89,7 @@
         }
     }
 
-    private final ObjectMapper objectMapper = new ObjectMapper();
+    private final ObjectMapper objectMapper = newObjectMapper();
     
     public void testIssue1604Simple() throws Exception
     {
diff --git a/src/test/java/com/fasterxml/jackson/databind/type/RecursiveTypeTest.java b/src/test/java/com/fasterxml/jackson/databind/type/RecursiveTypeTest.java
index f0643af..36b27f2 100644
--- a/src/test/java/com/fasterxml/jackson/databind/type/RecursiveTypeTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/type/RecursiveTypeTest.java
@@ -85,7 +85,7 @@
 
         assertNotNull(json);
 
-        // can not deserialize with current definition, however
+        // cannot deserialize with current definition, however
     }
 
     // for [databind#1301]
diff --git a/src/test/java/com/fasterxml/jackson/databind/type/TestAnnotatedClass.java b/src/test/java/com/fasterxml/jackson/databind/type/TestAnnotatedClass.java
index dd16220..c5fb1a5 100644
--- a/src/test/java/com/fasterxml/jackson/databind/type/TestAnnotatedClass.java
+++ b/src/test/java/com/fasterxml/jackson/databind/type/TestAnnotatedClass.java
@@ -2,8 +2,7 @@
 
 import com.fasterxml.jackson.annotation.*;
 import com.fasterxml.jackson.databind.*;
-import com.fasterxml.jackson.databind.introspect.AnnotatedClass;
-import com.fasterxml.jackson.databind.introspect.AnnotatedField;
+import com.fasterxml.jackson.databind.introspect.*;
 
 /**
  * Unit test for verifying that {@link AnnotatedClass}
@@ -82,7 +81,7 @@
     {
         SerializationConfig config = MAPPER.getSerializationConfig();
         JavaType t = MAPPER.constructType(FieldBean.class);
-        AnnotatedClass ac = AnnotatedClass.construct(t, config);
+        AnnotatedClass ac = AnnotatedClassResolver.resolve(config, t, config);
         // AnnotatedClass does not ignore non-visible fields, yet
         assertEquals(2, ac.getFieldCount());
         for (AnnotatedField f : ac.fields()) {
@@ -101,7 +100,27 @@
         Bean1005 bean = new Bean1005(13);
         SerializationConfig config = MAPPER.getSerializationConfig();
         JavaType t = MAPPER.constructType(bean.getClass());
-        AnnotatedClass ac = AnnotatedClass.construct(t, config);
+        AnnotatedClass ac = AnnotatedClassResolver.resolve(config, t, config);
         assertEquals(1, ac.getConstructors().size());
     }
+
+    public void testArrayTypeIntrospection() throws Exception
+    {
+        AnnotatedClass ac = AnnotatedClassResolver.resolve(MAPPER.getSerializationConfig(),
+                MAPPER.constructType(int[].class), null);
+        // 09-Jun-2017, tatu: During 2.9 development, access methods were failing at
+        //    certain points so
+        assertFalse(ac.memberMethods().iterator().hasNext());
+        assertFalse(ac.fields().iterator().hasNext());
+    }
+    
+    public void testIntrospectionWithRawClass() throws Exception
+    {
+        AnnotatedClass ac = AnnotatedClassResolver.resolveWithoutSuperTypes(MAPPER.getSerializationConfig(),
+                String.class, null);
+        // 09-Jun-2017, tatu: During 2.9 development, access methods were failing at
+        //    certain points so
+        assertFalse(ac.memberMethods().iterator().hasNext());
+        assertFalse(ac.fields().iterator().hasNext());
+    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/type/TestJavaType.java b/src/test/java/com/fasterxml/jackson/databind/type/TestJavaType.java
index 91be3f7..ffe0b29 100644
--- a/src/test/java/com/fasterxml/jackson/databind/type/TestJavaType.java
+++ b/src/test/java/com/fasterxml/jackson/databind/type/TestJavaType.java
@@ -72,6 +72,7 @@
         JavaType baseType = tf.constructType(BaseType.class);
         assertSame(BaseType.class, baseType.getRawClass());
         assertTrue(baseType.hasRawClass(BaseType.class));
+        assertFalse(baseType.isTypeOrSubTypeOf(SubType.class));
 
         assertFalse(baseType.isArrayType());
         assertFalse(baseType.isContainerType());
@@ -84,8 +85,28 @@
         assertNull(baseType.getContentType());
         assertNull(baseType.getKeyType());
         assertNull(baseType.getValueHandler());
+
+        assertEquals("Lcom/fasterxml/jackson/databind/type/TestJavaType$BaseType;", baseType.getGenericSignature());
+        assertEquals("Lcom/fasterxml/jackson/databind/type/TestJavaType$BaseType;", baseType.getErasedSignature());
     }
 
+    @SuppressWarnings("deprecation")
+    public void testDeprecated()
+    {
+        TypeFactory tf = TypeFactory.defaultInstance();
+        JavaType baseType = tf.constructType(BaseType.class);
+        assertTrue(baseType.hasRawClass(BaseType.class));
+        assertNull(baseType.getParameterSource());
+        assertNull(baseType.getContentTypeHandler());
+        assertNull(baseType.getContentValueHandler());
+        assertFalse(baseType.hasValueHandler());
+        assertFalse(baseType.hasHandlers());
+
+        assertSame(baseType, baseType.forcedNarrowBy(BaseType.class));
+        JavaType sub = baseType.forcedNarrowBy(SubType.class);
+        assertTrue(sub.hasRawClass(SubType.class));
+    }
+    
     public void testArrayType()
     {
         TypeFactory tf = TypeFactory.defaultInstance();
@@ -119,6 +140,9 @@
         assertNotNull(mapT.getContentType());
         assertNotNull(mapT.getKeyType());
 
+        assertEquals("Ljava/util/HashMap<Ljava/lang/Object;Ljava/lang/Object;>;", mapT.getGenericSignature());
+        assertEquals("Ljava/util/HashMap;", mapT.getErasedSignature());
+        
         assertTrue(mapT.equals(mapT));
         assertFalse(mapT.equals(null));
         assertFalse(mapT.equals("xyz"));
@@ -127,7 +151,17 @@
     public void testEnumType()
     {
         TypeFactory tf = TypeFactory.defaultInstance();
-        assertTrue(tf.constructType(MyEnum.class).isEnumType());
+        JavaType enumT = tf.constructType(MyEnum.class);
+        assertTrue(enumT.isEnumType());
+        assertFalse(enumT.hasHandlers());
+        assertTrue(enumT.isTypeOrSubTypeOf(MyEnum.class));
+        assertTrue(enumT.isTypeOrSubTypeOf(Object.class));
+        assertNull(enumT.containedType(3));
+        assertTrue(enumT.containedTypeOrUnknown(3).isJavaLangObject());
+
+        assertEquals("Lcom/fasterxml/jackson/databind/type/TestJavaType$MyEnum;", enumT.getGenericSignature());
+        assertEquals("Lcom/fasterxml/jackson/databind/type/TestJavaType$MyEnum;", enumT.getErasedSignature());
+
         assertTrue(tf.constructType(MyEnum2.class).isEnumType());
         assertTrue(tf.constructType(MyEnum.A.getClass()).isEnumType());
         assertTrue(tf.constructType(MyEnum2.A.getClass()).isEnumType());
@@ -164,7 +198,8 @@
         m = Generic1194.class.getMethod("getList");
         t  = tf.constructType(m.getGenericReturnType());
         assertEquals("Ljava/util/List<Ljava/lang/String;>;", t.getGenericSignature());
-
+        assertEquals("Ljava/util/List;", t.getErasedSignature());
+        
         m = Generic1194.class.getMethod("getMap");
         t  = tf.constructType(m.getGenericReturnType());
         assertEquals("Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;",
diff --git a/src/test/java/com/fasterxml/jackson/databind/type/TestTypeBindings.java b/src/test/java/com/fasterxml/jackson/databind/type/TestTypeBindings.java
index 7af37e8..6216842 100644
--- a/src/test/java/com/fasterxml/jackson/databind/type/TestTypeBindings.java
+++ b/src/test/java/com/fasterxml/jackson/databind/type/TestTypeBindings.java
@@ -40,10 +40,11 @@
     /**********************************************************
      */
 
+    private final TypeFactory DEFAULT_TF = TypeFactory.defaultInstance();
+
     public void testInnerType() throws Exception
     {
-        TypeFactory tf = TypeFactory.defaultInstance();
-        JavaType type = tf.constructType(InnerGenericTyping.InnerClass.class);
+        JavaType type = DEFAULT_TF.constructType(InnerGenericTyping.InnerClass.class);
         assertEquals(MapType.class, type.getClass());
         JavaType keyType = type.getKeyType();
         assertEquals(Object.class, keyType.getRawClass());
@@ -56,8 +57,34 @@
     // for [databind#76]
     public void testRecursiveType()
     {
-        TypeFactory tf = TypeFactory.defaultInstance();
-        JavaType type = tf.constructType(HashTree.class);
+        JavaType type = DEFAULT_TF.constructType(HashTree.class);
         assertNotNull(type);
     }
+
+    public void testBindingsBasics()
+    {
+        TypeBindings b = TypeBindings.create(Collection.class,
+                TypeFactory.unknownType());
+        // let's just call it -- should probably try to inspect but...
+        assertNotNull(b.toString());
+        assertEquals(Object.class, b.getBoundType(0).getRawClass());
+        assertNull(b.getBoundName(-1));
+        assertNull(b.getBoundType(-1));
+        assertNull(b.getBoundName(1));
+        assertNull(b.getBoundType(1));
+
+        assertFalse(b.equals("foo"));
+    }
+    
+    public void testInvalidBindings()
+    {
+        JavaType unknown = TypeFactory.unknownType();
+        try {
+            TypeBindings.create(AbstractType.class, unknown);
+            fail("Should not pass");
+        } catch (IllegalArgumentException e) {
+            verifyException(e, "Cannot create TypeBindings");
+            verifyException(e, "class expects 2");
+        }
+    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/type/TestTypeFactory.java b/src/test/java/com/fasterxml/jackson/databind/type/TestTypeFactory.java
index ab0ff60..60bcee8 100644
--- a/src/test/java/com/fasterxml/jackson/databind/type/TestTypeFactory.java
+++ b/src/test/java/com/fasterxml/jackson/databind/type/TestTypeFactory.java
@@ -192,14 +192,14 @@
             // Maps must take 2 type parameters, not just one
             tf.constructParametrizedType(Map.class, Map.class, strC);
         } catch (IllegalArgumentException e) {
-            verifyException(e, "Can not create TypeBindings for class java.util.Map");
+            verifyException(e, "Cannot create TypeBindings for class java.util.Map");
         }
 
         try {
             // Type only accepts one type param
             tf.constructParametrizedType(SingleArgGeneric.class, SingleArgGeneric.class, strC, strC);
         } catch (IllegalArgumentException e) {
-            verifyException(e, "Can not create TypeBindings for class ");
+            verifyException(e, "Cannot create TypeBindings for class ");
         }
     }
 
@@ -383,10 +383,6 @@
         assertEquals(Integer.class, subtype.getContentType().getContentType().getRawClass());
 
         // but with refinement, should have non-null super class
-        // 20-Oct-2015, tatu: For now refinement does not faithfully replicate the
-        //    structure, it only retains most important information. Here it means
-        //    that actually existing super-classes are skipped, and only original
-        //    type is linked as expected
 
         JavaType superType = subtype.getSuperClass();
         assertNotNull(superType);
@@ -397,6 +393,25 @@
         assertEquals(Integer.class, superType.getContentType().getContentType().getRawClass());
     }
 
+    public void testTypeGeneralization()
+    {
+        TypeFactory tf = newTypeFactory();
+        MapType t = tf.constructMapType(HashMap.class, String.class, Long.class);
+        JavaType superT = tf.constructGeneralizedType(t, Map.class);
+        assertEquals(String.class, superT.getKeyType().getRawClass());
+        assertEquals(Long.class, superT.getContentType().getRawClass());
+
+        assertSame(t, tf.constructGeneralizedType(t, HashMap.class));
+
+        // plus check there is super/sub relationship
+        try {
+            tf.constructGeneralizedType(t, TreeMap.class);
+            fail("Should not pass");
+        } catch (IllegalArgumentException e) {
+            verifyException(e, "not a super-type of");
+        }
+    }
+
     public void testMapTypesRaw()
     {
         TypeFactory tf = TypeFactory.defaultInstance();
diff --git a/src/test/java/com/fasterxml/jackson/databind/type/TestTypeFactory1604.java b/src/test/java/com/fasterxml/jackson/databind/type/TestTypeFactory1604.java
index 6bfec7c..4455547 100644
--- a/src/test/java/com/fasterxml/jackson/databind/type/TestTypeFactory1604.java
+++ b/src/test/java/com/fasterxml/jackson/databind/type/TestTypeFactory1604.java
@@ -96,7 +96,6 @@
     public void testErrorForMismatch()
     {
         TypeFactory tf = newTypeFactory();
-        
         // NOTE: plain `String` NOT `List<String>`
         JavaType base = tf.constructType(new TypeReference<Data1604<String>>() { });
 
@@ -109,10 +108,4 @@
             verifyException(e, "DataList1604");
         }
     }
-    
-    /*
-    static class TwoParam1604<KEY,VALUE> { }
-
-    static class SneakyTwoParam1604<V,K> extends TwoParam1604<K,List<V>> { }
-     */
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/type/TestTypeFactoryWithClassLoader.java b/src/test/java/com/fasterxml/jackson/databind/type/TestTypeFactoryWithClassLoader.java
index e9a8070..fa787ba 100644
--- a/src/test/java/com/fasterxml/jackson/databind/type/TestTypeFactoryWithClassLoader.java
+++ b/src/test/java/com/fasterxml/jackson/databind/type/TestTypeFactoryWithClassLoader.java
@@ -24,31 +24,31 @@
   private static ClassLoader classLoader;
   private static ClassLoader threadClassLoader;
   private static String aClassName;
-  private ObjectMapper objectMapper;
+  private ObjectMapper mapper;
 
-@BeforeClass
-public static void beforeClass() {
+  @BeforeClass
+  public static void beforeClass() {
 	  classLoader = AClass.class.getClassLoader();
 	  aClassName = AClass.getStaticClassName();
 	  threadClassLoader = Thread.currentThread().getContextClassLoader();
 	  Assert.assertNotNull(threadClassLoader);
-}
+  }
   
-@Before
-public void before() {
-  objectMapper = new ObjectMapper();
-}
+  @Before
+  public void before() {
+      mapper = new ObjectMapper();
+  }
 
-@After
-public void after() {
+  @After
+  public void after() {
 	Thread.currentThread().setContextClassLoader(threadClassLoader);
-	objectMapper = null;
-}
-	
-@Test
-public void testUsesCorrectClassLoaderWhenThreadClassLoaderIsNull() throws ClassNotFoundException {
+	mapper = null;
+  }
+
+  @Test
+  public void testUsesCorrectClassLoaderWhenThreadClassLoaderIsNull() throws ClassNotFoundException {
 	Thread.currentThread().setContextClassLoader(null);
-	TypeFactory spySut = spy(objectMapper.getTypeFactory().withModifier(typeModifier).withClassLoader(classLoader));
+	TypeFactory spySut = spy(mapper.getTypeFactory().withModifier(typeModifier).withClassLoader(classLoader));
 	Class<?> clazz = spySut.findClass(aClassName);
 	verify(spySut).getClassLoader();
 	verify(spySut).classForName(any(String.class), any(Boolean.class), eq(classLoader));
@@ -56,11 +56,11 @@
 	Assert.assertEquals(classLoader, spySut.getClassLoader());
 	Assert.assertEquals(typeModifier,spySut._modifiers[0]);
 	Assert.assertEquals(null, Thread.currentThread().getContextClassLoader());
-}
+  }
 
-@Test
+  @Test
 public void testUsesCorrectClassLoaderWhenThreadClassLoaderIsNotNull() throws ClassNotFoundException {
-	TypeFactory spySut = spy(objectMapper.getTypeFactory().withModifier(typeModifier).withClassLoader(classLoader));
+	TypeFactory spySut = spy(mapper.getTypeFactory().withModifier(typeModifier).withClassLoader(classLoader));
 	Class<?> clazz = spySut.findClass(aClassName);
 	verify(spySut).getClassLoader();
 	verify(spySut).classForName(any(String.class), any(Boolean.class), eq(classLoader));
@@ -71,42 +71,42 @@
 
 @Test
 public void testCallingOnlyWithModifierGivesExpectedResults(){
-	TypeFactory sut = objectMapper.getTypeFactory().withModifier(typeModifier);
+	TypeFactory sut = mapper.getTypeFactory().withModifier(typeModifier);
 	Assert.assertNull(sut.getClassLoader());
 	Assert.assertEquals(typeModifier,sut._modifiers[0]);
 }
 
 @Test
 public void testCallingOnlyWithClassLoaderGivesExpectedResults(){
-	TypeFactory sut = objectMapper.getTypeFactory().withClassLoader(classLoader);
+	TypeFactory sut = mapper.getTypeFactory().withClassLoader(classLoader);
 	Assert.assertNotNull(sut.getClassLoader());
 	Assert.assertArrayEquals(null,sut._modifiers);
 }
 
 @Test
 public void testDefaultTypeFactoryNotAffectedByWithConstructors() {
-	TypeFactory sut = objectMapper.getTypeFactory().withModifier(typeModifier).withClassLoader(classLoader);
+	TypeFactory sut = mapper.getTypeFactory().withModifier(typeModifier).withClassLoader(classLoader);
 	Assert.assertEquals(classLoader, sut.getClassLoader());
 	Assert.assertEquals(typeModifier,sut._modifiers[0]);
-	Assert.assertNull(objectMapper.getTypeFactory().getClassLoader());
-	Assert.assertArrayEquals(null,objectMapper.getTypeFactory()._modifiers);
+	Assert.assertNull(mapper.getTypeFactory().getClassLoader());
+	Assert.assertArrayEquals(null,mapper.getTypeFactory()._modifiers);
 }
 
 @Test
 public void testSetsTheCorrectClassLoderIfUsingWithModifierFollowedByWithClassLoader() {
-	TypeFactory sut = objectMapper.getTypeFactory().withModifier(typeModifier).withClassLoader(classLoader);
+	TypeFactory sut = mapper.getTypeFactory().withModifier(typeModifier).withClassLoader(classLoader);
 	Assert.assertNotNull(sut.getClassLoader());
 }
 
 @Test
 public void testSetsTheCorrectClassLoderIfUsingWithClassLoaderFollowedByWithModifier() {
-	TypeFactory sut = objectMapper.getTypeFactory().withClassLoader(classLoader).withModifier(typeModifier);
+	TypeFactory sut = mapper.getTypeFactory().withClassLoader(classLoader).withModifier(typeModifier);
 	Assert.assertNotNull(sut.getClassLoader());
 }
 
 @Test
 public void testThreadContextClassLoaderIsUsedIfNotUsingWithClassLoader() throws ClassNotFoundException {
-	TypeFactory spySut = spy(objectMapper.getTypeFactory());
+	TypeFactory spySut = spy(mapper.getTypeFactory());
 	Assert.assertNull(spySut.getClassLoader());
 	Class<?> clazz = spySut.findClass(aClassName);
 	Assert.assertNotNull(clazz);
@@ -116,7 +116,7 @@
 @Test
 public void testUsesFallBackClassLoaderIfNoThreadClassLoaderAndNoWithClassLoader() throws ClassNotFoundException {
 	Thread.currentThread().setContextClassLoader(null);
-	TypeFactory spySut = spy(objectMapper.getTypeFactory());
+	TypeFactory spySut = spy(mapper.getTypeFactory());
 	Assert.assertNull(spySut.getClassLoader());
 	Assert.assertArrayEquals(null,spySut._modifiers);
 	Class<?> clazz = spySut.findClass(aClassName);
@@ -124,24 +124,24 @@
 	verify(spySut).classForName(any(String.class));
 }
 
-public static class AClass
-{
-  private String _foo, _bar;
-  protected final static Class<?> thisClass = new Object() {
-  }.getClass().getEnclosingClass();
+    public static class AClass
+    {
+        private String _foo, _bar;
+        protected final static Class<?> thisClass = new Object() {
+        }.getClass().getEnclosingClass();
 
-  public AClass() { }
-  public AClass(String foo, String bar) {
-      _foo = foo;
-      _bar = bar;
-  }
-  public String getFoo() { return _foo; }
-  public String getBar() { return _bar; }
+        public AClass() { }
+        public AClass(String foo, String bar) {
+            _foo = foo;
+            _bar = bar;
+        }
+        public String getFoo() { return _foo; }
+        public String getBar() { return _bar; }
 
-  public void setFoo(String foo) { _foo = foo; }
-  public void setBar(String bar) { _bar = bar; }
-  public static String getStaticClassName() {
-	  return thisClass.getCanonicalName().replace("."+thisClass.getSimpleName(), "$"+thisClass.getSimpleName());
-  }
-}
+        public void setFoo(String foo) { _foo = foo; }
+        public void setBar(String bar) { _bar = bar; }
+        public static String getStaticClassName() {
+            return thisClass.getCanonicalName().replace("."+thisClass.getSimpleName(), "$"+thisClass.getSimpleName());
+        }
+    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/util/ArrayBuildersTest.java b/src/test/java/com/fasterxml/jackson/databind/util/ArrayBuildersTest.java
index a788e28..bae0285 100644
--- a/src/test/java/com/fasterxml/jackson/databind/util/ArrayBuildersTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/util/ArrayBuildersTest.java
@@ -1,12 +1,22 @@
 package com.fasterxml.jackson.databind.util;
 
+import java.util.Arrays;
+import java.util.HashSet;
+
 import org.junit.Assert;
 
 import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.util.ArrayBuilders.BooleanBuilder;
+import com.fasterxml.jackson.databind.util.ArrayBuilders.ByteBuilder;
+import com.fasterxml.jackson.databind.util.ArrayBuilders.DoubleBuilder;
+import com.fasterxml.jackson.databind.util.ArrayBuilders.FloatBuilder;
+import com.fasterxml.jackson.databind.util.ArrayBuilders.IntBuilder;
+import com.fasterxml.jackson.databind.util.ArrayBuilders.LongBuilder;
+import com.fasterxml.jackson.databind.util.ArrayBuilders.ShortBuilder;
 
 public class ArrayBuildersTest extends BaseMapTest
 {
-	// Test for [Issue#157]
+	// [databind#157]
 	public void testInsertInListNoDup()
 	{
         String [] arr = new String[]{"me", "you", "him"};
@@ -24,4 +34,57 @@
         newarr = ArrayBuilders.insertInListNoDup(arr, "foobar");
         Assert.assertArrayEquals(new String[]{"foobar", "me", "you", "him"}, newarr);
 	}
+
+     public void testBuilderAccess()
+     {
+         ArrayBuilders builders = new ArrayBuilders();
+
+         BooleanBuilder bb = builders.getBooleanBuilder();
+         assertNotNull(bb);
+         assertSame(bb, builders.getBooleanBuilder());
+
+         ByteBuilder b2 = builders.getByteBuilder();
+         assertNotNull(b2);
+         assertSame(b2, builders.getByteBuilder());
+
+         ShortBuilder sb = builders.getShortBuilder();
+         assertNotNull(sb);
+         assertSame(sb, builders.getShortBuilder());
+
+         IntBuilder ib = builders.getIntBuilder();
+         assertNotNull(ib);
+         assertSame(ib, builders.getIntBuilder());
+
+         LongBuilder lb = builders.getLongBuilder();
+         assertNotNull(lb);
+         assertSame(lb, builders.getLongBuilder());
+
+         FloatBuilder fb = builders.getFloatBuilder();
+         assertNotNull(fb);
+         assertSame(fb, builders.getFloatBuilder());
+
+         DoubleBuilder db = builders.getDoubleBuilder();
+         assertNotNull(db);
+         assertSame(db, builders.getDoubleBuilder());
+     }
+
+     public void testArrayComparator()
+     {
+         final int[] INT3 = new int[] { 3, 4, 5 };
+         Object comp = ArrayBuilders.getArrayComparator(INT3);
+         assertFalse(comp.equals(null));
+         assertTrue(comp.equals(INT3));
+         assertTrue(comp.equals(new int[] { 3, 4, 5 }));
+         assertFalse(comp.equals(new int[] { 5 }));
+         assertFalse(comp.equals(new int[] { 3, 4 }));
+         assertFalse(comp.equals(new int[] { 3, 5, 4 }));
+         assertFalse(comp.equals(new int[] { 3, 4, 5, 6 }));
+     }
+
+     public void testArraySet()
+     {
+         HashSet<String> set = ArrayBuilders.arrayToSet(new String[] { "foo", "bar" });
+         assertEquals(2, set.size());
+         assertEquals(new HashSet<String>(Arrays.asList("bar", "foo")), set);
+     }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/util/BeanUtilTest.java b/src/test/java/com/fasterxml/jackson/databind/util/BeanUtilTest.java
new file mode 100644
index 0000000..afd1732
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/util/BeanUtilTest.java
@@ -0,0 +1,147 @@
+package com.fasterxml.jackson.databind.util;
+
+import java.util.*;
+import java.util.concurrent.atomic.AtomicReference;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+
+public class BeanUtilTest extends BaseMapTest
+{
+    static class IsGetters {
+        public boolean isPrimitive() { return false; }
+        public Boolean isWrapper() { return false; }
+        public String isNotGetter() { return null; }
+        public boolean is() { return false; }
+    }
+
+    static class Getters {
+        public String getCallbacks() { return null; }
+        public String getMetaClass() { return null; }
+        public boolean get() { return false; }
+    }
+    
+    static class Setters {
+        public void setFoo() { }
+        public void notSetter() { }
+        public void set() { }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    public void testNameMangle()
+    {
+        assertEquals("foo", BeanUtil.legacyManglePropertyName("getFoo", 3));
+        assertEquals("foo", BeanUtil.stdManglePropertyName("getFoo", 3));
+
+        assertEquals("url", BeanUtil.legacyManglePropertyName("getURL", 3));
+        assertEquals("URL", BeanUtil.stdManglePropertyName("getURL", 3));
+    }
+
+    public void testGetDefaultValue()
+    {
+        TypeFactory tf = TypeFactory.defaultInstance();
+        // For collection/array/Map types, should give `NOT_EMPTY`:
+        assertEquals(JsonInclude.Include.NON_EMPTY,
+                BeanUtil.getDefaultValue(tf.constructType(Map.class)));
+        assertEquals(JsonInclude.Include.NON_EMPTY,
+                BeanUtil.getDefaultValue(tf.constructType(List.class)));
+        assertEquals(JsonInclude.Include.NON_EMPTY,
+                BeanUtil.getDefaultValue(tf.constructType(Object[].class)));
+        // as well as ReferenceTypes, String
+        assertEquals(JsonInclude.Include.NON_EMPTY,
+                BeanUtil.getDefaultValue(tf.constructType(AtomicReference.class)));
+        assertEquals("",
+                BeanUtil.getDefaultValue(tf.constructType(String.class)));
+        // primitive/wrappers have others
+        assertEquals(Integer.valueOf(0),
+                BeanUtil.getDefaultValue(tf.constructType(Integer.class)));
+        
+
+        // but POJOs have no real default
+        assertNull(BeanUtil.getDefaultValue(tf.constructType(getClass())));
+    }
+
+    public void testIsGetter() throws Exception
+    {
+        _testIsGetter("isPrimitive", "primitive");
+        _testIsGetter("isWrapper", "wrapper");
+        _testIsGetter("isNotGetter", null);
+        _testIsGetter("is", null);
+    }
+
+    public void testOkNameForGetter() throws Exception
+    {
+        // mostly chosen to exercise groovy exclusion
+        _testOkNameForGetter("getCallbacks", "callbacks");
+        _testOkNameForGetter("getMetaClass", "metaClass");
+        _testOkNameForGetter("get", null);
+    }
+    
+    public void testOkNameForSetter() throws Exception
+    {
+        _testOkNameForSetter("setFoo", "foo");
+        _testOkNameForSetter("notSetter", null);
+        _testOkNameForSetter("set", null);
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+
+    private void _testIsGetter(String name, String expName) throws Exception {
+        _testIsGetter(name, expName, true);
+        _testIsGetter(name, expName, false);
+    }
+
+    private void _testIsGetter(String name, String expName, boolean useStd) throws Exception
+    {
+        AnnotatedMethod m = _method(IsGetters.class, name);
+        if (expName == null) {
+            assertNull(BeanUtil.okNameForIsGetter(m, name, useStd));
+        } else {
+            assertEquals(expName, BeanUtil.okNameForIsGetter(m, name, useStd));
+        }
+    }
+
+    private void _testOkNameForGetter(String name, String expName) throws Exception {
+        _testOkNameForGetter(name, expName, true);
+        _testOkNameForGetter(name, expName, false);
+    }
+
+    private void _testOkNameForGetter(String name, String expName, boolean useStd) throws Exception {
+        AnnotatedMethod m = _method(Getters.class, name);
+        if (expName == null) {
+            assertNull(BeanUtil.okNameForGetter(m, useStd));
+        } else {
+            assertEquals(expName, BeanUtil.okNameForGetter(m, useStd));
+        }
+    }
+
+    private void _testOkNameForSetter(String name, String expName) throws Exception {
+        _testOkNameForSetter(name, expName, true);
+        _testOkNameForSetter(name, expName, false);
+    }
+
+    @SuppressWarnings("deprecation")
+    private void _testOkNameForSetter(String name, String expName, boolean useStd) throws Exception {
+        AnnotatedMethod m = _method(Setters.class, name);
+        if (expName == null) {
+            assertNull(BeanUtil.okNameForSetter(m, useStd));
+        } else {
+            assertEquals(expName, BeanUtil.okNameForSetter(m, useStd));
+        }
+    }
+    
+    private AnnotatedMethod _method(Class<?> cls, String name, Class<?>...parameterTypes) throws Exception {
+        return new AnnotatedMethod(null, cls.getMethod(name, parameterTypes), null, null);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/util/ByteBufferUtilsTest.java b/src/test/java/com/fasterxml/jackson/databind/util/ByteBufferUtilsTest.java
new file mode 100644
index 0000000..da66aa4
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/util/ByteBufferUtilsTest.java
@@ -0,0 +1,28 @@
+package com.fasterxml.jackson.databind.util;
+
+import java.nio.ByteBuffer;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+
+public class ByteBufferUtilsTest extends BaseMapTest
+{
+    public void testByteBufferInput() throws Exception {
+        byte[] input = new byte[] { 1, 2, 3 };
+        ByteBufferBackedInputStream wrapped = new ByteBufferBackedInputStream(ByteBuffer.wrap(input));
+        assertEquals(3, wrapped.available());
+        assertEquals(1, wrapped.read());
+        byte[] buffer = new byte[10];
+        assertEquals(2, wrapped.read(buffer, 0, 5));
+        wrapped.close();
+    }
+
+    public void testByteBufferOutput() throws Exception {
+        ByteBuffer b = ByteBuffer.wrap(new byte[10]);
+        ByteBufferBackedOutputStream wrappedOut = new ByteBufferBackedOutputStream(b);
+        wrappedOut.write(1);
+        wrappedOut.write(new byte[] { 2, 3 });
+        assertEquals(3, b.position());
+        assertEquals(7, b.remaining());
+        wrappedOut.close();
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/util/TestClassUtil.java b/src/test/java/com/fasterxml/jackson/databind/util/ClassUtilTest.java
similarity index 81%
rename from src/test/java/com/fasterxml/jackson/databind/util/TestClassUtil.java
rename to src/test/java/com/fasterxml/jackson/databind/util/ClassUtilTest.java
index b02f6ac..61faf3b 100644
--- a/src/test/java/com/fasterxml/jackson/databind/util/TestClassUtil.java
+++ b/src/test/java/com/fasterxml/jackson/databind/util/ClassUtilTest.java
@@ -5,8 +5,7 @@
 import com.fasterxml.jackson.databind.BaseMapTest;
 import com.fasterxml.jackson.databind.util.ClassUtil;
 
-public class TestClassUtil
-    extends BaseMapTest
+public class ClassUtilTest extends BaseMapTest
 {
     /*
     /**********************************************************
@@ -91,13 +90,6 @@
             assertSame(e, e2);
         }
 
-        try {
-            ClassUtil.throwRootCause(e);
-            fail("Shouldn't get this far");
-        } catch (Exception eAct) {
-            assertSame(e, eAct);
-        }
-
         Error err = new Error();
         try {
             ClassUtil.throwAsIAE(err);
@@ -137,7 +129,7 @@
         }
     }
 
-    public void testPrimiteDefaultValue()
+    public void testPrimitiveDefaultValue()
     {
         assertEquals(Integer.valueOf(0), ClassUtil.defaultValue(Integer.TYPE));
         assertEquals(Long.valueOf(0L), ClassUtil.defaultValue(Long.TYPE));
@@ -148,6 +140,8 @@
         assertEquals(Double.valueOf(0.0), ClassUtil.defaultValue(Double.TYPE));
         assertEquals(Float.valueOf(0.0f), ClassUtil.defaultValue(Float.TYPE));
 
+        assertEquals(Boolean.FALSE, ClassUtil.defaultValue(Boolean.TYPE));
+        
         try {
             ClassUtil.defaultValue(String.class);
         } catch (IllegalArgumentException e) {
@@ -155,34 +149,54 @@
         }
     }
 
-    public void testPrimiteWrapperType()
+    public void testPrimitiveWrapperType()
     {
+        assertEquals(Byte.class, ClassUtil.wrapperType(Byte.TYPE));
+        assertEquals(Short.class, ClassUtil.wrapperType(Short.TYPE));
+        assertEquals(Character.class, ClassUtil.wrapperType(Character.TYPE));
         assertEquals(Integer.class, ClassUtil.wrapperType(Integer.TYPE));
         assertEquals(Long.class, ClassUtil.wrapperType(Long.TYPE));
-        assertEquals(Character.class, ClassUtil.wrapperType(Character.TYPE));
-        assertEquals(Short.class, ClassUtil.wrapperType(Short.TYPE));
-        assertEquals(Byte.class, ClassUtil.wrapperType(Byte.TYPE));
 
         assertEquals(Double.class, ClassUtil.wrapperType(Double.TYPE));
         assertEquals(Float.class, ClassUtil.wrapperType(Float.TYPE));
 
+        assertEquals(Boolean.class, ClassUtil.wrapperType(Boolean.TYPE));
+        
         try {
             ClassUtil.wrapperType(String.class);
+            fail("Should not pass");
         } catch (IllegalArgumentException e) {
             verifyException(e, "String is not a primitive type");
         }
     }
 
+    public void testWrapperToPrimitiveType()
+    {
+        assertEquals(Integer.TYPE, ClassUtil.primitiveType(Integer.class));
+        assertEquals(Long.TYPE, ClassUtil.primitiveType(Long.class));
+        assertEquals(Character.TYPE, ClassUtil.primitiveType(Character.class));
+        assertEquals(Short.TYPE, ClassUtil.primitiveType(Short.class));
+        assertEquals(Byte.TYPE, ClassUtil.primitiveType(Byte.class));
+        assertEquals(Float.TYPE, ClassUtil.primitiveType(Float.class));
+        assertEquals(Double.TYPE, ClassUtil.primitiveType(Double.class));
+        assertEquals(Boolean.TYPE, ClassUtil.primitiveType(Boolean.class));
+        
+        assertNull(ClassUtil.primitiveType(String.class));
+    }
+
     public void testFindEnumType()
     {
         assertEquals(TestEnum.class, ClassUtil.findEnumType(TestEnum.A));
+        // different codepaths for empty and non-empty EnumSets...
         assertEquals(TestEnum.class, ClassUtil.findEnumType(EnumSet.allOf(TestEnum.class)));
+        assertEquals(TestEnum.class, ClassUtil.findEnumType(EnumSet.noneOf(TestEnum.class)));
+
         assertEquals(TestEnum.class, ClassUtil.findEnumType(new EnumMap<TestEnum,Integer>(TestEnum.class)));
     }
 
     public void testDescs()
     {
-        final String exp = String.class.getName();
+        final String exp = "`java.lang.String`";
         assertEquals(exp, ClassUtil.getClassDescription("foo"));
         assertEquals(exp, ClassUtil.getClassDescription(String.class));
     }
diff --git a/src/test/java/com/fasterxml/jackson/databind/util/CompactStringObjectMapTest.java b/src/test/java/com/fasterxml/jackson/databind/util/CompactStringObjectMapTest.java
new file mode 100644
index 0000000..325d2f2
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/util/CompactStringObjectMapTest.java
@@ -0,0 +1,28 @@
+package com.fasterxml.jackson.databind.util;
+
+import java.util.*;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+
+public class CompactStringObjectMapTest extends BaseMapTest
+{
+    public void testBig()
+    {
+        Map<String,String> all = new LinkedHashMap<>();
+        for (int i = 0; i < 1000; ++i) {
+            String key = "key"+i;
+            all.put(key, key);
+        }
+        CompactStringObjectMap map = CompactStringObjectMap.construct(all);
+        assertEquals(1000, map.keys().size());
+
+        for (String key : all.keySet()) {
+            assertEquals(key, map.find(key));
+        }
+
+        // and then bogus empty keys
+        assertNull(map.find("key1000"));
+        assertNull(map.find("keyXXX"));
+        assertNull(map.find(""));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/util/EnumValuesTest.java b/src/test/java/com/fasterxml/jackson/databind/util/EnumValuesTest.java
new file mode 100644
index 0000000..ccc3ea2
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/util/EnumValuesTest.java
@@ -0,0 +1,66 @@
+package com.fasterxml.jackson.databind.util;
+
+import java.util.List;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationConfig;
+import com.fasterxml.jackson.databind.SerializationFeature;
+
+public class EnumValuesTest extends BaseMapTest
+{
+    enum ABC {
+        A("A"),
+        B("b"),
+        C("C");
+
+        private final String desc;
+
+        private ABC(String d) { desc = d; }
+
+        @Override
+        public String toString() { return desc; }
+    }
+
+    final ObjectMapper MAPPER = new ObjectMapper();
+
+    @SuppressWarnings("unchecked")
+    public void testConstructFromName() {
+        SerializationConfig cfg = MAPPER.getSerializationConfig()
+                .without(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
+        Class<Enum<?>> enumClass = (Class<Enum<?>>)(Class<?>) ABC.class;
+        EnumValues values = EnumValues.construct(cfg, enumClass);
+        assertEquals("A", values.serializedValueFor(ABC.A).toString());
+        assertEquals("B", values.serializedValueFor(ABC.B).toString());
+        assertEquals("C", values.serializedValueFor(ABC.C).toString());
+        assertEquals(3, values.values().size());
+        assertEquals(3, values.internalMap().size());
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testConstructWithToString() {
+        SerializationConfig cfg = MAPPER.getSerializationConfig()
+                .with(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
+        Class<Enum<?>> enumClass = (Class<Enum<?>>)(Class<?>) ABC.class;
+        EnumValues values = EnumValues.construct(cfg, enumClass);
+        assertEquals("A", values.serializedValueFor(ABC.A).toString());
+        assertEquals("b", values.serializedValueFor(ABC.B).toString());
+        assertEquals("C", values.serializedValueFor(ABC.C).toString());
+        assertEquals(3, values.values().size());
+        assertEquals(3, values.internalMap().size());
+    }
+
+    public void testEnumResolver()
+    {
+        EnumResolver enumRes = EnumResolver.constructUnsafeUsingToString(ABC.class, null);
+        assertEquals(ABC.B, enumRes.getEnum(1));
+        assertNull(enumRes.getEnum(-1));
+        assertNull(enumRes.getEnum(3));
+        assertEquals(2, enumRes.lastValidIndex());
+        List<Enum<?>> enums = enumRes.getEnums();
+        assertEquals(3, enums.size());
+        assertEquals(ABC.A, enums.get(0));
+        assertEquals(ABC.B, enums.get(1));
+        assertEquals(ABC.C, enums.get(2));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/util/ISO8601DateFormatTest.java b/src/test/java/com/fasterxml/jackson/databind/util/ISO8601DateFormatTest.java
index 1264292..20e9412 100644
--- a/src/test/java/com/fasterxml/jackson/databind/util/ISO8601DateFormatTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/util/ISO8601DateFormatTest.java
@@ -6,9 +6,7 @@
 
 import com.fasterxml.jackson.databind.BaseMapTest;
 
-/**
- * @see ISO8601DateFormat
- */
+@SuppressWarnings("deprecation")
 public class ISO8601DateFormatTest extends BaseMapTest
 {
     private ISO8601DateFormat df;
diff --git a/src/test/java/com/fasterxml/jackson/databind/util/ISO8601UtilsTest.java b/src/test/java/com/fasterxml/jackson/databind/util/ISO8601UtilsTest.java
index 6680048..5ce6751 100644
--- a/src/test/java/com/fasterxml/jackson/databind/util/ISO8601UtilsTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/util/ISO8601UtilsTest.java
@@ -10,10 +10,9 @@
 import java.util.TimeZone;
 import java.util.concurrent.TimeUnit;
 
-/**
- * @see ISO8601Utils
- */
-public class ISO8601UtilsTest extends BaseMapTest {
+@SuppressWarnings("deprecation")
+public class ISO8601UtilsTest extends BaseMapTest
+{
     private Date date;
     private Date dateWithoutTime;
     private Date dateZeroMillis;
diff --git a/src/test/java/com/fasterxml/jackson/databind/util/JsonParserSequenceTest.java b/src/test/java/com/fasterxml/jackson/databind/util/JsonParserSequenceTest.java
new file mode 100644
index 0000000..b7b6228
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/util/JsonParserSequenceTest.java
@@ -0,0 +1,45 @@
+package com.fasterxml.jackson.databind.util;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.core.util.JsonParserSequence;
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class JsonParserSequenceTest extends BaseMapTest {
+
+    private final ObjectMapper MAPPER = objectMapper();
+
+    /**
+     * Verifies fix for [core#372]
+     */
+    @SuppressWarnings("resource")
+    public void testJsonParserSequenceOverridesSkipChildren() throws Exception
+    {
+        // Create parser from TokenBuffer containing an incomplete JSON object
+        TokenBuffer buf1 = new TokenBuffer(MAPPER, false);
+        buf1.writeStartObject();
+        buf1.writeFieldName("foo");
+        buf1.writeStartObject();
+        JsonParser parser1 = buf1.asParser();
+
+        // Create parser from second TokenBuffer that completes the object started by the first buffer
+        TokenBuffer buf2 = new TokenBuffer(MAPPER, false);
+        buf2.writeEndObject();
+        buf2.writeEndObject();
+        JsonParser parser2 = buf2.asParser();
+
+        // Create sequence of both parsers and verify tokens
+        JsonParser parserSequence = JsonParserSequence.createFlattened(false, parser1, parser2);
+        assertToken(JsonToken.START_OBJECT, parserSequence.nextToken());
+        assertToken(JsonToken.FIELD_NAME, parserSequence.nextToken());
+        assertToken(JsonToken.START_OBJECT, parserSequence.nextToken());
+
+        // Skip children of current token. JsonParserSequence's overridden version should switch to the next parser
+        // in the sequence
+        parserSequence.skipChildren();
+
+        // Verify last token
+        assertToken(JsonToken.END_OBJECT, parserSequence.nextToken());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/util/NameTransformerTest.java b/src/test/java/com/fasterxml/jackson/databind/util/NameTransformerTest.java
new file mode 100644
index 0000000..648c8a9
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/util/NameTransformerTest.java
@@ -0,0 +1,23 @@
+package com.fasterxml.jackson.databind.util;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+
+public class NameTransformerTest extends BaseMapTest
+{
+    public void testSimpleTransformer() throws Exception
+    {
+        NameTransformer xfer;
+
+        xfer = NameTransformer.simpleTransformer("a", null);
+        assertEquals("aFoo", xfer.transform("Foo"));
+        assertEquals("Foo", xfer.reverse("aFoo"));
+
+        xfer = NameTransformer.simpleTransformer(null, "++");
+        assertEquals("foo++", xfer.transform("foo"));
+        assertEquals("foo", xfer.reverse("foo++"));
+
+        xfer = NameTransformer.simpleTransformer("(", ")");
+        assertEquals("(foo)", xfer.transform("foo"));
+        assertEquals("foo", xfer.reverse("(foo)"));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/util/RawValueTest.java b/src/test/java/com/fasterxml/jackson/databind/util/RawValueTest.java
new file mode 100644
index 0000000..4d652b0
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/util/RawValueTest.java
@@ -0,0 +1,24 @@
+package com.fasterxml.jackson.databind.util;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.JsonSerializable;
+
+public class RawValueTest extends BaseMapTest
+{
+    public void testEquality()
+    {
+        RawValue raw1 = new RawValue("foo");
+        RawValue raw1b = new RawValue("foo");
+        RawValue raw2 = new RawValue("bar");
+
+        assertTrue(raw1.equals(raw1));
+        assertTrue(raw1.equals(raw1b));
+
+        assertFalse(raw1.equals(raw2));
+        assertFalse(raw1.equals(null));
+
+        assertFalse(new RawValue((JsonSerializable) null).equals(raw1));
+
+        assertNotNull(raw1.toString());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/util/TestStdDateFormat.java b/src/test/java/com/fasterxml/jackson/databind/util/TestStdDateFormat.java
new file mode 100644
index 0000000..ec99545
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/util/TestStdDateFormat.java
@@ -0,0 +1,142 @@
+package com.fasterxml.jackson.databind.util;
+
+import java.text.ParseException;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.util.StdDateFormat;
+
+public class TestStdDateFormat
+    extends BaseMapTest
+{
+    @SuppressWarnings("deprecation")
+    public void testFactories() {
+        TimeZone tz = TimeZone.getTimeZone("GMT");
+        Locale loc = Locale.US;
+        assertNotNull(StdDateFormat.getISO8601Format(tz, loc));
+        assertNotNull(StdDateFormat.getRFC1123Format(tz, loc));
+    }
+
+    // [databind#803]
+    public void testLenientDefaults() throws Exception
+    {
+        StdDateFormat f = StdDateFormat.instance;
+
+        // default should be lenient
+        assertTrue(f.isLenient());
+
+        StdDateFormat f2 = f.clone();
+        assertTrue(f2.isLenient());
+
+        f2.setLenient(false);
+        assertFalse(f2.isLenient());
+
+        f2.setLenient(true);
+        assertTrue(f2.isLenient());
+
+        // and for testing, finally, leave as non-lenient
+        f2.setLenient(false);
+        assertFalse(f2.isLenient());
+        StdDateFormat f3 = f2.clone();
+        assertFalse(f3.isLenient());
+    }
+
+    public void testISO8601RegexpDateOnly() throws Exception
+    {
+        Pattern p = StdDateFormat.PATTERN_PLAIN;
+        Matcher m = p.matcher("1997-07-16");
+        assertTrue(m.matches());
+        // no matching groups...
+    }
+
+    public void testISO8601RegexpFull() throws Exception
+    {
+        /*
+        String PATTERN_PLAIN_STR = "\\d\\d\\d\\d[-]\\d\\d[-]\\d\\d";
+        Pattern PATTERN_ISO8601 = Pattern.compile(PATTERN_PLAIN_STR
+                +"[T]\\d\\d[:]\\d\\d(?:[:]\\d\\d)?" // hours, minutes, optional seconds
+                +"(\\.\\d+)?" // optional second fractions
+                +"(Z|[+-]\\d\\d(?:[:]?\\d\\d)?)?" // optional timeoffset/Z
+                );
+        final Pattern p = PATTERN_ISO8601;
+        */
+        final Pattern p = StdDateFormat.PATTERN_ISO8601;
+        Matcher m;
+
+        // First simple full representation (except no millisecs)
+        m = p.matcher("1997-07-16T19:20:00+01:00");
+        assertTrue(m.matches());
+        assertEquals(2, m.groupCount());
+        assertNull(m.group(1)); // no match (why not empty String)
+        assertEquals("+01:00", m.group(2));
+
+        // Then with 'Z' instead
+        m = p.matcher("1997-07-16T19:20:00Z");
+        assertTrue(m.matches());
+        assertNull(m.group(1));
+        assertEquals("Z", m.group(2));
+
+        // Then drop seconds too
+        m = p.matcher("1997-07-16T19:20+01:00");
+        assertTrue(m.matches());
+        assertNull(m.group(1));
+        assertEquals("+01:00", m.group(2));
+
+        // Full with milliseconds:
+        m = p.matcher("1997-07-16T19:20:00.2+03:00");
+        assertTrue(m.matches());
+        assertEquals(2, m.groupCount());
+        assertEquals(".2", m.group(1));
+        assertEquals("+03:00", m.group(2));
+        
+        m = p.matcher("1972-12-28T00:00:00.01-0300");
+        assertTrue(m.matches());
+        assertEquals(".01", m.group(1));
+        assertEquals("-0300", m.group(2));
+
+        m = p.matcher("1972-12-28T00:00:00.400+00");
+        assertTrue(m.matches());
+        assertEquals(".400", m.group(1));
+        assertEquals("+00", m.group(2));
+
+        // and then drop time offset AND seconds
+        m = p.matcher("1972-12-28T04:15");
+        assertTrue(m.matches());
+        assertNull(m.group(1));
+        assertNull(m.group(2));
+    }
+
+    public void testLenientParsing() throws Exception
+    {
+        StdDateFormat f = StdDateFormat.instance.clone();
+        f.setLenient(false);
+
+        // first, legal dates are... legal
+        Date dt = f.parse("2015-11-30");
+        assertNotNull(dt);
+
+        // but as importantly, when not lenient, do not allow
+        try {
+            f.parse("2015-11-32");
+            fail("Should not pass");
+        } catch (ParseException e) {
+            verifyException(e, "Cannot parse date");
+        }
+
+        // ... yet, with lenient, do allow
+        f.setLenient(true);
+        dt = f.parse("2015-11-32");
+        assertNotNull(dt);
+    }
+    
+    public void testInvalid() {
+        StdDateFormat std = new StdDateFormat();
+        try {
+            std.parse("foobar");
+        } catch (java.text.ParseException e) {
+            verifyException(e, "Cannot parse");
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/util/TestTokenBuffer.java b/src/test/java/com/fasterxml/jackson/databind/util/TestTokenBuffer.java
index 477301e..e785be7 100644
--- a/src/test/java/com/fasterxml/jackson/databind/util/TestTokenBuffer.java
+++ b/src/test/java/com/fasterxml/jackson/databind/util/TestTokenBuffer.java
@@ -1,30 +1,61 @@
 package com.fasterxml.jackson.databind.util;
 
 import java.io.*;
+import java.math.BigDecimal;
+import java.math.BigInteger;
 import java.util.UUID;
 
 import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.JsonParser.NumberType;
+import com.fasterxml.jackson.core.io.SerializedString;
 import com.fasterxml.jackson.core.util.JsonParserSequence;
-import com.fasterxml.jackson.databind.BaseMapTest;
-import com.fasterxml.jackson.databind.ObjectMapper;
+
+import com.fasterxml.jackson.databind.*;
 
 public class TestTokenBuffer extends BaseMapTest
 {
     private final ObjectMapper MAPPER = objectMapper();
-    
+
+    static class Base1730 { }
+
+    static class Sub1730 extends Base1730 { }
+
     /*
     /**********************************************************
     /* Basic TokenBuffer tests
     /**********************************************************
      */
-    
+
+    public void testBasicConfig() throws IOException
+    {
+        TokenBuffer buf;
+
+        buf = new TokenBuffer(MAPPER, false);
+        assertEquals(MAPPER.version(), buf.version());
+        assertSame(MAPPER, buf.getCodec());
+        assertNotNull(buf.getOutputContext());
+        assertFalse(buf.isClosed());
+
+        buf.setCodec(null);
+        assertNull(buf.getCodec());
+
+        assertFalse(buf.isEnabled(JsonGenerator.Feature.ESCAPE_NON_ASCII));
+        buf.enable(JsonGenerator.Feature.ESCAPE_NON_ASCII);
+        assertTrue(buf.isEnabled(JsonGenerator.Feature.ESCAPE_NON_ASCII));
+        buf.disable(JsonGenerator.Feature.ESCAPE_NON_ASCII);
+        assertFalse(buf.isEnabled(JsonGenerator.Feature.ESCAPE_NON_ASCII));
+
+        buf.close();
+        assertTrue(buf.isClosed());
+    }
+
     /**
      * Test writing of individual simple values
      */
     public void testSimpleWrites() throws IOException
     {
         TokenBuffer buf = new TokenBuffer(null, false); // no ObjectCodec
-
+        
         // First, with empty buffer
         JsonParser p = buf.asParser();
         assertNull(p.getCurrentToken());
@@ -34,7 +65,6 @@
         // Then with simple text
         buf.writeString("abc");
 
-        // First, simple text
         p = buf.asParser();
         assertNull(p.getCurrentToken());
         assertToken(JsonToken.VALUE_STRING, p.nextToken());
@@ -54,6 +84,100 @@
         buf.close();
     }
 
+    // For 2.9, explicit "isNaN" check
+    public void testSimpleNumberWrites() throws IOException
+    {
+        TokenBuffer buf = new TokenBuffer(null, false);
+
+        double[] values1 = new double[] {
+                0.25, Double.NaN, -2.0, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY
+        };
+        float[] values2 = new float[] {
+                Float.NEGATIVE_INFINITY,
+                0.25f,
+                Float.POSITIVE_INFINITY
+        };
+
+        for (double v : values1) {
+            buf.writeNumber(v);
+        }
+        for (float v : values2) {
+            buf.writeNumber(v);
+        }
+
+        JsonParser p = buf.asParser();
+        assertNull(p.getCurrentToken());
+
+        for (double v : values1) {
+            assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
+            double actual = p.getDoubleValue();
+            boolean expNan = Double.isNaN(v) || Double.isInfinite(v);
+            assertEquals(expNan, p.isNaN());
+            assertEquals(0, Double.compare(v, actual));
+        }
+        for (float v : values2) {
+            assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
+            float actual = p.getFloatValue();
+            boolean expNan = Float.isNaN(v) || Float.isInfinite(v);
+            assertEquals(expNan, p.isNaN());
+            assertEquals(0, Float.compare(v, actual));
+        }
+        p.close();
+        buf.close();
+    }
+
+    // [databind#1729]
+    public void testNumberOverflowInt() throws IOException
+    {
+        try (TokenBuffer buf = new TokenBuffer(null, false)) {
+            long big = 1L + Integer.MAX_VALUE;
+            buf.writeNumber(big);
+            try (JsonParser p = buf.asParser()) {
+                assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+                assertEquals(NumberType.LONG, p.getNumberType());
+                try {
+                    p.getIntValue();
+                    fail("Expected failure for `int` overflow");
+                } catch (JsonParseException e) {
+                    verifyException(e, "Numeric value ("+big+") out of range of int");
+                }
+            }
+        }
+        // and ditto for coercion.
+        try (TokenBuffer buf = new TokenBuffer(null, false)) {
+            long big = 1L + Integer.MAX_VALUE;
+            buf.writeNumber(String.valueOf(big));
+            try (JsonParser p = buf.asParser()) {
+                // NOTE: oddity of buffering, no inspection of "real" type if given String...
+                assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
+                try {
+                    p.getIntValue();
+                    fail("Expected failure for `int` overflow");
+                } catch (JsonParseException e) {
+                    verifyException(e, "Numeric value ("+big+") out of range of int");
+                }
+            }
+        }
+    }
+
+    public void testNumberOverflowLong() throws IOException
+    {
+        try (TokenBuffer buf = new TokenBuffer(null, false)) {
+            BigInteger big = BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE);
+            buf.writeNumber(big);
+            try (JsonParser p = buf.asParser()) {
+                assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+                assertEquals(NumberType.BIG_INTEGER, p.getNumberType());
+                try {
+                    p.getLongValue();
+                    fail("Expected failure for `long` overflow");
+                } catch (JsonParseException e) {
+                    verifyException(e, "Numeric value ("+big+") out of range of long");
+                }
+            }
+        }
+    }
+
     public void testParentContext() throws IOException
     {
         TokenBuffer buf = new TokenBuffer(null, false); // no ObjectCodec
@@ -200,6 +324,11 @@
         verifyJsonSpecSampleDoc(tb.asParser(), true);
         tb.close();
         p.close();
+
+    
+        // 19-Oct-2016, tatu: Just for fun, trigger `toString()` for code coverage
+        String desc = tb.toString();
+        assertNotNull(desc);
     }
 
     public void testAppend() throws IOException
@@ -382,6 +511,39 @@
         buf.close();
     }
 
+    public void testBasicSerialize() throws IOException
+    {
+        TokenBuffer buf;
+
+        // let's see how empty works...
+        buf = new TokenBuffer(MAPPER, false);
+        assertEquals("", MAPPER.writeValueAsString(buf));
+        buf.close();
+        
+        buf = new TokenBuffer(MAPPER, false);
+        buf.writeStartArray();
+        buf.writeBoolean(true);
+        buf.writeBoolean(false);
+        long l = 1L + Integer.MAX_VALUE;
+        buf.writeNumber(l);
+        buf.writeNumber((short) 4);
+        buf.writeNumber(0.5);
+        buf.writeEndArray();
+        assertEquals(aposToQuotes("[true,false,"+l+",4,0.5]"), MAPPER.writeValueAsString(buf));
+        buf.close();
+
+        buf = new TokenBuffer(MAPPER, false);
+        buf.writeStartObject();
+        buf.writeFieldName(new SerializedString("foo"));
+        buf.writeNull();
+        buf.writeFieldName("bar");
+        buf.writeNumber(BigInteger.valueOf(123));
+        buf.writeFieldName("dec");
+        buf.writeNumber(BigDecimal.valueOf(5).movePointLeft(2));
+        assertEquals(aposToQuotes("{'foo':null,'bar':123,'dec':0.05}"), MAPPER.writeValueAsString(buf));
+        buf.close();
+    }
+
     /*
     /**********************************************************
     /* Tests to verify interaction of TokenBuffer and JsonParserSequence
@@ -468,7 +630,7 @@
         buf2.close();
         buf3.close();
         buf4.close();
-    }    
+    }
 
     // [databind#743]
     public void testRawValues() throws Exception
@@ -487,4 +649,20 @@
         // then verify it would be serialized just fine
         assertEquals(RAW, MAPPER.writeValueAsString(buf));
     }
+
+    // [databind#1730]
+    public void testEmbeddedObjectCoerceCheck() throws Exception
+    {
+        TokenBuffer buf = new TokenBuffer(null, false);
+        Object inputPojo = new Sub1730();
+        buf.writeEmbeddedObject(inputPojo);
+
+        // first: raw value won't be transformed in any way:
+        JsonParser p = buf.asParser();
+        Base1730 out = MAPPER.readValue(p, Base1730.class);
+
+        assertSame(inputPojo, out);
+        p.close();
+        buf.close();
+    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/views/DefaultViewTest.java b/src/test/java/com/fasterxml/jackson/databind/views/DefaultViewTest.java
new file mode 100644
index 0000000..34e8c08
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/views/DefaultViewTest.java
@@ -0,0 +1,71 @@
+package com.fasterxml.jackson.databind.views;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+
+// for [databind#507], supporting default views
+public class DefaultViewTest extends BaseMapTest
+{
+    // Classes that represent views
+    static class ViewA { }
+    static class ViewAA extends ViewA { }
+    static class ViewB { }
+    static class ViewBB extends ViewB { }
+
+    @JsonView(ViewA.class)
+    @JsonPropertyOrder({ "a", "b" })
+    static class Defaulting {
+        public int a = 3;
+
+        @JsonView(ViewB.class)
+        public int b = 5;
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */    
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+
+    public void testDeserialization() throws IOException
+    {
+        final String JSON = aposToQuotes("{'a':1,'b':2}");
+
+        // first: no views:
+        Defaulting result = MAPPER.readerFor(Defaulting.class)
+                .readValue(JSON);
+        assertEquals(result.a, 1);
+        assertEquals(result.b, 2);
+
+        // Then views; first A, then B(B)
+        result = MAPPER.readerFor(Defaulting.class)
+                .withView(ViewA.class)
+                .readValue(JSON);
+        assertEquals(result.a, 1);
+        assertEquals(result.b, 5);
+
+        result = MAPPER.readerFor(Defaulting.class)
+                .withView(ViewBB.class)
+                .readValue(JSON);
+        assertEquals(result.a, 3);
+        assertEquals(result.b, 2);
+    }
+
+    public void testSerialization() throws IOException
+    {
+        assertEquals(aposToQuotes("{'a':3,'b':5}"),
+                MAPPER.writeValueAsString(new Defaulting()));
+
+        assertEquals(aposToQuotes("{'a':3}"),
+                MAPPER.writerWithView(ViewA.class)
+                    .writeValueAsString(new Defaulting()));
+        assertEquals(aposToQuotes("{'b':5}"),
+                MAPPER.writerWithView(ViewB.class)
+                    .writeValueAsString(new Defaulting()));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/views/TestViewDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/views/TestViewDeserialization.java
index 5c28728..fd81d13 100644
--- a/src/test/java/com/fasterxml/jackson/databind/views/TestViewDeserialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/views/TestViewDeserialization.java
@@ -1,17 +1,13 @@
 package com.fasterxml.jackson.databind.views;
 
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.annotation.JsonView;
 
 import com.fasterxml.jackson.databind.*;
 
 public class TestViewDeserialization extends BaseMapTest
 {
-    /*
-    /**********************************************************
-    /* Helper types
-    /**********************************************************
-     */
-
     // Classes that represent views
     static class ViewA { }
     static class ViewAA extends ViewA { }
@@ -40,6 +36,23 @@
         public int b;
     }
 
+    static class ViewsAndCreatorBean
+    {
+        @JsonView(ViewA.class)
+        public int a;
+
+        @JsonView(ViewB.class)
+        public int b;
+
+        @JsonCreator
+        public ViewsAndCreatorBean(@JsonProperty("a") int a,
+                @JsonProperty("b") int b)
+        {
+            this.a = a;
+            this.b = b;
+        }
+    }
+
     /*
     /************************************************************************ 
     /* Tests
@@ -101,4 +114,28 @@
         assertEquals(0, bean.a);
         assertEquals(2, bean.b);
     }
+
+    public void testWithCreatorAndViews() throws Exception
+    {
+        ViewsAndCreatorBean result; 
+
+        result = mapper.readerFor(ViewsAndCreatorBean.class)
+                .withView(ViewA.class)
+                .readValue(aposToQuotes("{'a':1,'b':2}"));
+        assertEquals(1, result.a);
+        assertEquals(0, result.b);
+
+        result = mapper.readerFor(ViewsAndCreatorBean.class)
+                .withView(ViewB.class)
+                .readValue(aposToQuotes("{'a':1,'b':2}"));
+        assertEquals(0, result.a);
+        assertEquals(2, result.b);
+
+        // and actually... fine to skip incompatible stuff too
+        result = mapper.readerFor(ViewsAndCreatorBean.class)
+                .withView(ViewB.class)
+                .readValue(aposToQuotes("{'a':[ 1, 23, { } ],'b':2}"));
+        assertEquals(0, result.a);
+        assertEquals(2, result.b);
+    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/views/TestViewSerialization.java b/src/test/java/com/fasterxml/jackson/databind/views/TestViewSerialization.java
index 564eb41..79dfee0 100644
--- a/src/test/java/com/fasterxml/jackson/databind/views/TestViewSerialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/views/TestViewSerialization.java
@@ -14,12 +14,6 @@
 public class TestViewSerialization
     extends BaseMapTest
 {
-    /*
-    /**********************************************************
-    /* Helper types
-    /**********************************************************
-     */
-
     // Classes that represent views
     static class ViewA { }
     static class ViewAA extends ViewA { }
@@ -68,58 +62,63 @@
         public String value = "x";
     }   
 
-    // [JACKSON-868]
     public static class WebView { }
     public static class OtherView { }
     public static class Foo {
         @JsonView(WebView.class)
         public int getFoo() { return 3; }
     }
-    
+
     /*
     /**********************************************************
     /* Unit tests
     /**********************************************************
      */    
-    
+
+    private final ObjectMapper MAPPER = objectMapper();
+
     @SuppressWarnings("unchecked")
     public void testSimple() throws IOException
     {
         StringWriter sw = new StringWriter();
-        ObjectMapper mapper = new ObjectMapper();
         // Ok, first, using no view whatsoever; all 3
         Bean bean = new Bean();
-        Map<String,Object> map = writeAndMap(mapper, bean);
+        Map<String,Object> map = writeAndMap(MAPPER, bean);
         assertEquals(3, map.size());
 
         // Then with "ViewA", just one property
         sw = new StringWriter();
-        mapper.writerWithView(ViewA.class).writeValue(sw, bean);
-        map = mapper.readValue(sw.toString(), Map.class);
+        MAPPER.writerWithView(ViewA.class).writeValue(sw, bean);
+        map = MAPPER.readValue(sw.toString(), Map.class);
         assertEquals(1, map.size());
         assertEquals("1", map.get("a"));
 
         // "ViewAA", 2 properties
         sw = new StringWriter();
-        mapper.writerWithView(ViewAA.class).writeValue(sw, bean);
-        map = mapper.readValue(sw.toString(), Map.class);
+        MAPPER.writerWithView(ViewAA.class).writeValue(sw, bean);
+        map = MAPPER.readValue(sw.toString(), Map.class);
         assertEquals(2, map.size());
         assertEquals("1", map.get("a"));
         assertEquals("2", map.get("aa"));
 
         // "ViewB", 2 prop2
-        String json = mapper.writerWithView(ViewB.class).writeValueAsString(bean);
-        map = mapper.readValue(json, Map.class);
+        String json = MAPPER.writerWithView(ViewB.class).writeValueAsString(bean);
+        map = MAPPER.readValue(json, Map.class);
         assertEquals(2, map.size());
         assertEquals("2", map.get("aa"));
         assertEquals("3", map.get("b"));
 
         // and "ViewBB", 2 as well
-        json = mapper.writerWithView(ViewBB.class).writeValueAsString(bean);
-        map = mapper.readValue(json, Map.class);
+        json = MAPPER.writerWithView(ViewBB.class).writeValueAsString(bean);
+        map = MAPPER.readValue(json, Map.class);
         assertEquals(2, map.size());
         assertEquals("2", map.get("aa"));
         assertEquals("3", map.get("b"));
+
+        // and finally, without view.
+        json = MAPPER.writerWithView(null).writeValueAsString(bean);
+        map = MAPPER.readValue(json, Map.class);
+        assertEquals(3, map.size());
     }
 
     /**
@@ -132,25 +131,31 @@
     public void testDefaultExclusion() throws IOException
     {
         MixedBean bean = new MixedBean();
-        StringWriter sw = new StringWriter();
 
-        ObjectMapper mapper = new ObjectMapper();
         // default setting: both fields will get included
-        mapper.writerWithView(ViewA.class).writeValue(sw, bean);
-        Map<String,Object> map = mapper.readValue(sw.toString(), Map.class);
+        String json = MAPPER.writerWithView(ViewA.class).writeValueAsString(bean);
+        Map<String,Object> map = MAPPER.readValue(json, Map.class);
         assertEquals(2, map.size());
         assertEquals("1", map.get("a"));
         assertEquals("2", map.get("b"));
 
         // but can also change (but not necessarily on the fly...)
-        mapper = new ObjectMapper();
+        ObjectMapper mapper = new ObjectMapper();
         mapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false);
+
         // with this setting, only explicit inclusions count:
-        String json = mapper.writerWithView(ViewA.class).writeValueAsString(bean);
+        json = mapper.writerWithView(ViewA.class).writeValueAsString(bean);
         map = mapper.readValue(json, Map.class);
         assertEquals(1, map.size());
         assertEquals("1", map.get("a"));
         assertNull(map.get("b"));
+
+        // but without view, view processing disabled:
+        json = mapper.writer().withView(null).writeValueAsString(bean);
+        map = mapper.readValue(json, Map.class);
+        assertEquals(2, map.size());
+        assertEquals("1", map.get("a"));
+        assertEquals("2", map.get("b"));
     }
 
     /**
@@ -159,15 +164,15 @@
      */
     public void testImplicitAutoDetection() throws Exception
     {
-        assertEquals("{\"a\":1}", objectMapper().writeValueAsString(new ImplicitBean()));
+        assertEquals("{\"a\":1}",
+                MAPPER.writeValueAsString(new ImplicitBean()));
     }
 
     public void testVisibility() throws Exception
     {
-        ObjectMapper mapper = new ObjectMapper();
         VisibilityBean bean = new VisibilityBean();
         // Without view setting, should only see "id"
-        String json = mapper.writerWithView(Object.class).writeValueAsString(bean);
+        String json = MAPPER.writerWithView(Object.class).writeValueAsString(bean);
         //json = mapper.writeValueAsString(bean);
         assertEquals("{\"id\":\"id\"}", json);
     }
diff --git a/src/test/java/com/fasterxml/jackson/databind/views/TestViewsSerialization2.java b/src/test/java/com/fasterxml/jackson/databind/views/TestViewsSerialization2.java
index eac65b2..96b11cf 100644
--- a/src/test/java/com/fasterxml/jackson/databind/views/TestViewsSerialization2.java
+++ b/src/test/java/com/fasterxml/jackson/databind/views/TestViewsSerialization2.java
@@ -9,59 +9,16 @@
 public class TestViewsSerialization2 extends BaseMapTest
 {
     /*
-    /************************************************************************ 
-    /* Tests
-    /************************************************************************ 
+    /************************************************************************
+    /* Helper classes
+    /************************************************************************
      */
-    
-  public void testDataBindingUsage( ) throws Exception
-  {
-    ObjectMapper objectMapper = createObjectMapper( null );
-    String result = serializeWithObjectMapper(new ComplexTestData( ), Views.View.class, objectMapper );
-    assertEquals(-1, result.indexOf( "nameHidden" ));
-  }
 
-  public void testDataBindingUsageWithoutView( ) throws Exception
-  {
-    ObjectMapper objectMapper = createObjectMapper( null );
-    String json = serializeWithObjectMapper(new ComplexTestData( ), null, objectMapper);
-    assertTrue(json.indexOf( "nameHidden" ) > 0);
-  }
-
-  /*
-  /************************************************************************
-  /* Helper  methods
-  /************************************************************************
-   */
-
-  private ObjectMapper createObjectMapper(Class<?> viewClass)
-  {
-    ObjectMapper objectMapper = new ObjectMapper( );
-    objectMapper.configure( SerializationFeature.FAIL_ON_EMPTY_BEANS, false );
-    objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL );
-    objectMapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false );
-//    objectMapper.getSerializationConfig( ).disable( SerializationConfig.SerializationFeature.DEFAULT_VIEW_INCLUSION );
-//    objectMapper.getSerializationConfig( ).setSerializationView( viewClass );
-    return objectMapper;
-  }
-  
-  private String serializeWithObjectMapper(Object object, Class<? extends Views.View> view, ObjectMapper objectMapper )
-      throws IOException
-  {
-    return objectMapper.writerWithView(view).writeValueAsString(object);
-  }
-
-  /*
-  /************************************************************************
-  /* Helper classes
-  /************************************************************************
-   */
-
-  static class Views
-  {
-    public interface View { }
-    public interface ExtendedView  extends View { }
-  }
+    static class Views
+    {
+        public interface View { }
+        public interface ExtendedView  extends View { }
+    }
   
   static class ComplexTestData
   {
@@ -156,6 +113,47 @@
     {
       this.nameHidden = nameHidden;
     }
-  }
+    }
 
-}
\ No newline at end of file
+    /*
+    /************************************************************************ 
+    /* Tests
+    /************************************************************************ 
+     */
+  
+    public void testDataBindingUsage( ) throws Exception
+    {
+        ObjectMapper mapper = createMapper();
+        String result = serializeWithObjectMapper(new ComplexTestData( ), Views.View.class, mapper);
+        assertEquals(-1, result.indexOf( "nameHidden" ));
+    }
+
+    public void testDataBindingUsageWithoutView( ) throws Exception
+    {
+        ObjectMapper mapper = createMapper();
+        String json = serializeWithObjectMapper(new ComplexTestData( ), null, mapper);
+        assertTrue(json.indexOf( "nameHidden" ) > 0);
+    }
+
+    /*
+    /************************************************************************
+    /* Helper  methods
+    /************************************************************************
+     */
+
+    private ObjectMapper createMapper()
+    {
+        ObjectMapper mapper = newObjectMapper();
+        mapper.configure( SerializationFeature.FAIL_ON_EMPTY_BEANS, false );
+        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL );
+        mapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false );
+        return mapper;
+    }
+
+    private String serializeWithObjectMapper(Object object, Class<? extends Views.View> view, ObjectMapper mapper )
+            throws IOException
+    {
+        return mapper.writerWithView(view).writeValueAsString(object);
+    }
+
+  }
\ No newline at end of file
diff --git a/src/test/java/com/fasterxml/jackson/failing/AnyPropSorting518Test.java b/src/test/java/com/fasterxml/jackson/failing/AnyPropSorting518Test.java
new file mode 100644
index 0000000..dbef399
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/AnyPropSorting518Test.java
@@ -0,0 +1,49 @@
+package com.fasterxml.jackson.failing;
+
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonAnyGetter;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import com.fasterxml.jackson.databind.*;
+
+public class AnyPropSorting518Test extends BaseMapTest
+{
+    @JsonPropertyOrder(alphabetic = true)
+    static class Bean
+    {
+        public int b;
+
+        protected Map<String,Object> extra = new HashMap<>();
+
+        public int a;
+
+        public Bean(int a, int b, Map<String,Object> x) {
+            this.a = a;
+            this.b = b;
+            extra = x;
+        }
+
+        @JsonAnyGetter
+        public Map<String,Object> getExtra() { return extra; }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+
+    public void testAnyBeanWithSort() throws Exception
+    {
+        Map<String,Object> extra = new LinkedHashMap<>();
+        extra.put("y", 4);
+        extra.put("x", 3);
+        String json = MAPPER.writeValueAsString(new Bean(1, 2, extra));
+        assertEquals(aposToQuotes("{'a':1,'b':2,'x':3,'y':4}"),
+                json);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/failing/AnySetter1035Test.java b/src/test/java/com/fasterxml/jackson/failing/AnySetter1035Test.java
deleted file mode 100644
index 451cab3..0000000
--- a/src/test/java/com/fasterxml/jackson/failing/AnySetter1035Test.java
+++ /dev/null
@@ -1,94 +0,0 @@
-package com.fasterxml.jackson.failing;
-
-import java.util.*;
-
-import com.fasterxml.jackson.annotation.*;
-
-import com.fasterxml.jackson.databind.*;
-
-/**
- * Test for [databind#1035], wherein key type of a `Map` not used with `@JsonAnySetter`
- * (value type is).
- */
-public class AnySetter1035Test extends BaseMapTest
-{
-    static class MyGeneric<T>
-    {
-        private String staticallyMappedProperty;
-        private Map<T, Integer> dynamicallyMappedProperties = new HashMap<T, Integer>();
-
-        public String getStaticallyMappedProperty() {
-            return staticallyMappedProperty;
-        }
-
-        @JsonAnySetter
-        public void addDynamicallyMappedProperty(T key, int value) {
-            dynamicallyMappedProperties.put(key, value);
-        }
-
-        public void setStaticallyMappedProperty(String staticallyMappedProperty) {
-            this.staticallyMappedProperty = staticallyMappedProperty;
-        }
-
-        @JsonAnyGetter
-        public Map<T, Integer> getDynamicallyMappedProperties() {
-            return dynamicallyMappedProperties;
-        }
-    }
-
-    static class MyWrapper
-    {
-        private MyGeneric<String> myStringGeneric;
-        private MyGeneric<Integer> myIntegerGeneric;
-
-        public MyGeneric<String> getMyStringGeneric() {
-            return myStringGeneric;
-        }
-
-        public void setMyStringGeneric(MyGeneric<String> myStringGeneric) {
-            this.myStringGeneric = myStringGeneric;
-        }
-
-        public MyGeneric<Integer> getMyIntegerGeneric() {
-            return myIntegerGeneric;
-        }
-
-        public void setMyIntegerGeneric(MyGeneric<Integer> myIntegerGeneric) {
-            this.myIntegerGeneric = myIntegerGeneric;
-        }
-    }
-
-    public void testGenericAnySetter() throws Exception
-    {
-        ObjectMapper mapper = new ObjectMapper();
-
-        Map<String, Integer> stringGenericMap = new HashMap<String, Integer>();
-        stringGenericMap.put("testStringKey", 5);
-        Map<Integer, Integer> integerGenericMap = new HashMap<Integer, Integer>();
-        integerGenericMap.put(111, 6);
-
-        MyWrapper deserialized = mapper.readValue(aposToQuotes(
-                "{'myStringGeneric':{'staticallyMappedProperty':'Test','testStringKey':5},'myIntegerGeneric':{'staticallyMappedProperty':'Test2','111':6}}"
-                ), MyWrapper.class);
-        MyGeneric<String> stringGeneric = deserialized.getMyStringGeneric();
-        MyGeneric<Integer> integerGeneric = deserialized.getMyIntegerGeneric();
-
-        assertNotNull(stringGeneric);
-        assertEquals(stringGeneric.getStaticallyMappedProperty(), "Test");
-        for(Map.Entry<String, Integer> entry : stringGeneric.getDynamicallyMappedProperties().entrySet()) {
-            assertTrue("A key in MyGeneric<String> is not an String.", entry.getKey() instanceof String);
-            assertTrue("A value in MyGeneric<Integer> is not an Integer.", entry.getValue() instanceof Integer);
-        }
-        assertEquals(stringGeneric.getDynamicallyMappedProperties(), stringGenericMap);
-
-        assertNotNull(integerGeneric);
-        assertEquals(integerGeneric.getStaticallyMappedProperty(), "Test2");
-        for(Map.Entry<Integer, Integer> entry : integerGeneric.getDynamicallyMappedProperties().entrySet()) {
-            Object key = entry.getKey();
-            assertEquals("A key in MyGeneric<Integer> is not an Integer.", Integer.class, key.getClass());
-            Object value = entry.getValue();
-            assertEquals("A value in MyGeneric<Integer> is not an Integer.", Integer.class, value.getClass());
-        }
-        assertEquals(integerGeneric.getDynamicallyMappedProperties(), integerGenericMap);
-    }
-}
diff --git a/src/test/java/com/fasterxml/jackson/failing/BackReference1516Test.java b/src/test/java/com/fasterxml/jackson/failing/BackReference1516Test.java
index 95c4de7..23e298b 100644
--- a/src/test/java/com/fasterxml/jackson/failing/BackReference1516Test.java
+++ b/src/test/java/com/fasterxml/jackson/failing/BackReference1516Test.java
@@ -74,7 +74,7 @@
     private final String PARENT_CHILD_JSON = aposToQuotes(
 "{ 'id': 'abc',\n"+
 "  'name': 'Bob',\n"+
-"  'child': { 'id': 'def', 'title':'Bert' }\n"+
+"  'child': { 'id': 'def', 'name':'Bert' }\n"+
 "}");
     
     public void testWithParentCreator() throws Exception
@@ -82,6 +82,8 @@
         ParentWithCreator result = MAPPER.readValue(PARENT_CHILD_JSON,
                 ParentWithCreator.class);
         assertNotNull(result);
+        assertNotNull(result.child);
+        assertSame(result, result.child.parent);
     }
 
     public void testWithParentNoCreator() throws Exception
@@ -89,5 +91,7 @@
         ParentWithoutCreator result = MAPPER.readValue(PARENT_CHILD_JSON,
                 ParentWithoutCreator.class);
         assertNotNull(result);
+        assertNotNull(result.child);
+        assertSame(result, result.child.parent);
     }
 }
diff --git a/src/test/java/com/fasterxml/jackson/failing/CollectionType1415Test.java b/src/test/java/com/fasterxml/jackson/failing/CollectionType1415Test.java
deleted file mode 100644
index dcf8718..0000000
--- a/src/test/java/com/fasterxml/jackson/failing/CollectionType1415Test.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package com.fasterxml.jackson.failing;
-
-import java.util.*;
-
-import com.fasterxml.jackson.databind.BaseMapTest;
-import com.fasterxml.jackson.databind.JavaType;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.type.CollectionType;
-import com.fasterxml.jackson.databind.type.MapType;
-
-// for [databind#1415]
-public class CollectionType1415Test extends BaseMapTest
-{
-    static abstract class LongList implements List<Long> { }
-
-    static abstract class StringLongMap implements Map<String,Long> { }
-
-    /*
-    /**********************************************************
-    /* Unit tests
-    /**********************************************************
-     */
-
-    private final ObjectMapper MAPPER = new ObjectMapper();
-
-    public void testExplicitCollectionType() throws Exception
-    {
-        JavaType t = MAPPER.getTypeFactory()
-                .constructCollectionType(LongList.class, Long.class);
-        assertEquals(LongList.class, t.getRawClass());
-        assertEquals(Long.class, t.getContentType().getRawClass());
-    }
-
-    public void testImplicitCollectionType() throws Exception
-    {
-        JavaType t = MAPPER.getTypeFactory()
-                .constructParametricType(List.class, Long.class);
-        assertEquals(CollectionType.class, t.getClass());
-        assertEquals(List.class, t.getRawClass());
-        assertEquals(Long.class, t.getContentType().getRawClass());
-    }
-
-    public void testExplicitMapType() throws Exception
-    {
-        JavaType t = MAPPER.getTypeFactory()
-                .constructMapType(StringLongMap.class,
-                        String.class, Long.class);
-        assertEquals(StringLongMap.class, t.getRawClass());
-        assertEquals(String.class, t.getKeyType().getRawClass());
-        assertEquals(Long.class, t.getContentType().getRawClass());
-    }
-
-    public void testImplicitMapType() throws Exception
-    {
-        JavaType t = MAPPER.getTypeFactory()
-                .constructParametricType(Map.class, Long.class, Boolean.class);
-        assertEquals(MapType.class, t.getClass());
-        assertEquals(Long.class, t.getKeyType().getRawClass());
-        assertEquals(Boolean.class, t.getContentType().getRawClass());
-    }
-}
diff --git a/src/test/java/com/fasterxml/jackson/failing/CreatorProperties1401Test.java b/src/test/java/com/fasterxml/jackson/failing/CreatorAnySetter1401Test.java
similarity index 91%
rename from src/test/java/com/fasterxml/jackson/failing/CreatorProperties1401Test.java
rename to src/test/java/com/fasterxml/jackson/failing/CreatorAnySetter1401Test.java
index 7671519..b8fb645 100644
--- a/src/test/java/com/fasterxml/jackson/failing/CreatorProperties1401Test.java
+++ b/src/test/java/com/fasterxml/jackson/failing/CreatorAnySetter1401Test.java
@@ -4,9 +4,9 @@
 
 import com.fasterxml.jackson.databind.*;
 
-// for [databind#1401]: should allow "Any Setter" to back up otherwise problematic
-// Creator properties?
-public class CreatorProperties1401Test extends BaseMapTest
+// for [databind#1401]: should allow "Any Setter" to back up otherwise
+// problematic Creator properties?
+public class CreatorAnySetter1401Test extends BaseMapTest
 {
     // for [databind#1401]
     static class NoSetter1401 {
diff --git a/src/test/java/com/fasterxml/jackson/failing/DefaultTypingOverride1391Test.java b/src/test/java/com/fasterxml/jackson/failing/DefaultTypingOverride1391Test.java
new file mode 100644
index 0000000..40d4a31
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/DefaultTypingOverride1391Test.java
@@ -0,0 +1,29 @@
+package com.fasterxml.jackson.failing;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.databind.*;
+
+// for [databind#1391]: should allow disabling of default typing
+// via explicit {@link JsonTypeInfo}
+public class DefaultTypingOverride1391Test extends BaseMapTest
+{
+    static class ListWrapper {
+        /* 03-Oct-2016, tatu: This doesn't work because it applies to contents
+         *   (elements), NOT the container. But there is no current mechanism
+         *   to change that; need to add a new feature or properties in 2.9
+         */
+        @JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
+        public Collection<String> stuff = Collections.emptyList();
+    }
+
+    public void testCollectionWithOverride() throws Exception
+    {
+        final ObjectMapper mapper = new ObjectMapper()
+            .enableDefaultTypingAsProperty(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE,
+                    "$type");
+        String json = mapper.writeValueAsString(new ListWrapper());
+        assertEquals(aposToQuotes("{'stuff':[]}"), json);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/failing/EnumAsExternalPropertyId1328Test.java b/src/test/java/com/fasterxml/jackson/failing/EnumAsExternalPropertyId1328Test.java
deleted file mode 100644
index e0510dd..0000000
--- a/src/test/java/com/fasterxml/jackson/failing/EnumAsExternalPropertyId1328Test.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package com.fasterxml.jackson.failing;
-
-import com.fasterxml.jackson.annotation.*;
-
-import com.fasterxml.jackson.databind.*;
-
-// Test for [databind#1328]; does not actually reproduce the issue at this point.
-public class EnumAsExternalPropertyId1328Test extends BaseMapTest
-{
-    static class Bean1328 {
-        public Type1328 type;
-
-        @JsonTypeInfo(property = "type", include = JsonTypeInfo.As.EXTERNAL_PROPERTY, use = JsonTypeInfo.Id.NAME,
-                visible=true)
-        @JsonSubTypes({
-                  @JsonSubTypes.Type(value = A.class,name = "A"),
-                  @JsonSubTypes.Type(value = B.class,name = "B")
-        })
-        public Interface1328 obj;
-    }
-
-    static interface Interface1328 { }
-
-    enum Type1328 {
-        A, B;
-    }
-
-   static class A implements Interface1328 {
-       public int a;
-   }
-
-   static class B implements Interface1328 {
-       public int b;
-   }
-
-   /*
-    /**********************************************************
-    /* Test methods
-    /**********************************************************
-     */
-
-    private final ObjectMapper MAPPER = new ObjectMapper();
-
-    public void testExternalTypeIdAsEnum() throws Exception
-    {
-        final String JSON = aposToQuotes("{ 'type':'A', 'obj': { 'a' : 4 } }'");
-        Bean1328 result = MAPPER.readValue(JSON, Bean1328.class);
-        assertNotNull(result.obj);
-        assertSame(A.class, result.obj.getClass());
-        assertEquals(4, ((A) result.obj).a);
-
-        assertSame(Type1328.A, result.type);
-    }
-}
diff --git a/src/test/java/com/fasterxml/jackson/failing/EnumDeserialization1626Test.java b/src/test/java/com/fasterxml/jackson/failing/EnumDeserialization1626Test.java
new file mode 100644
index 0000000..ef7625d
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/EnumDeserialization1626Test.java
@@ -0,0 +1,87 @@
+package com.fasterxml.jackson.failing;
+
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * NOTE: not assumed to be actual bug -- real numbers are not coerced into
+ * Strings, and are instead assumed to always mean index numbers.
+ * But test retained in case there might be ways to improve support
+ * here: as is, one MUST use Creator method to resolve from number to
+ * enum.
+ */
+public class EnumDeserialization1626Test extends BaseMapTest
+{
+    static class JsonResponseEnvelope<T> {
+        @JsonProperty("d")
+        public T data;
+    }
+
+    static class ShippingMethodInfo {
+        @JsonProperty("typeId")
+        public int typeId;
+
+        @JsonProperty("value")
+        private ShippingMethods value;
+
+        @JsonProperty("coverage")
+        public int coverage;
+    }
+
+    enum ShippingMethods {
+        @JsonProperty("0")
+        SHIPPING_METHODS_UNSPECIFIED(0),
+
+        @JsonProperty("10")
+        SHIPPING_METHODS_FED_EX_PRIORITY_OVERNIGHT(10),
+
+        @JsonProperty("17")
+        SHIPPING_METHODS_FED_EX_1DAY_FREIGHT(17),
+        ;
+
+        private final int shippingMethodId;
+
+        ShippingMethods(final int shippingMethodId) {
+            this.shippingMethodId = shippingMethodId;
+        }
+
+        public int getShippingMethodId() {
+            return shippingMethodId;
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    protected final ObjectMapper MAPPER = new ObjectMapper();
+
+    // [databind#1626]
+    public void testSparseNumericEnum626() throws Exception
+    {
+        String jsonResponse =
+                "{\n" +
+                        "    \"d\": [\n" +
+                        "        {\n" +
+                        "            \"typeId\": 0,\n" +
+                        // NOTE! Only real number fails; quoted-as-String is bound as expected
+                        "            \"value\": 17,\n" +
+                        "            \"coverage\": 1"+
+                        "        }\n" +
+                        "     ]\n" +
+                        "}";
+
+        JsonResponseEnvelope<List<ShippingMethodInfo>> mappedResponse =
+                MAPPER.readValue(jsonResponse,
+                        new TypeReference<JsonResponseEnvelope<List<ShippingMethodInfo>>>() { });
+        List<ShippingMethodInfo> shippingMethods = mappedResponse.data;
+
+        assertEquals(ShippingMethods.SHIPPING_METHODS_FED_EX_1DAY_FREIGHT,
+                shippingMethods.get(0).value);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/failing/ExternalTypeIdWithUnwrapped2039Test.java b/src/test/java/com/fasterxml/jackson/failing/ExternalTypeIdWithUnwrapped2039Test.java
new file mode 100644
index 0000000..a81f6ab
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/ExternalTypeIdWithUnwrapped2039Test.java
@@ -0,0 +1,58 @@
+package com.fasterxml.jackson.failing;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+
+public class ExternalTypeIdWithUnwrapped2039Test extends BaseMapTest
+{
+    static class MainType2039 {
+        public String text;
+
+        @JsonUnwrapped public Wrapped2039 wrapped;
+
+        @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "subtype")
+        @JsonSubTypes({ @JsonSubTypes.Type(value = SubA2039.class, name = "SubA") })
+        public SubType2039 sub;
+
+        public void setSub(SubType2039 s) {
+            sub = s;
+        }
+
+        public void setWrapped(Wrapped2039 w) {
+            wrapped = w;
+        }
+    }
+
+    static class Wrapped2039 {
+        public String wrapped;
+    }
+
+    public static class SubType2039 { }
+
+    public static class SubA2039 extends SubType2039 {
+        @JsonProperty public boolean bool;
+    }
+    
+    public void testExternalWithUnwrapped2039() throws Exception
+    {
+        final ObjectMapper mapper = newObjectMapper();
+
+        final String json = aposToQuotes("{\n"
+                +"'text': 'this is A',\n"
+                +"'wrapped': 'yes',\n"
+                +"'subtype': 'SubA',\n"
+                +"'sub': {\n"
+                +"  'bool': true\n"
+                +"}\n"
+                +"}");
+        final MainType2039 main = mapper.readValue(json, MainType2039.class);
+
+        assertEquals("this is A", main.text);
+        assertEquals("yes", main.wrapped.wrapped);
+
+        assertNotNull(main.sub);
+        assertEquals(SubA2039.class, main.sub.getClass()); // <- fails here
+        assertEquals(true, ((SubA2039) main.sub).bool);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/failing/ImplicitParamsForCreator806Test.java b/src/test/java/com/fasterxml/jackson/failing/ImplicitParamsForCreator806Test.java
index 509e572..890f576 100644
--- a/src/test/java/com/fasterxml/jackson/failing/ImplicitParamsForCreator806Test.java
+++ b/src/test/java/com/fasterxml/jackson/failing/ImplicitParamsForCreator806Test.java
@@ -24,7 +24,7 @@
         protected int x, y;
 
         // annotation should NOT be needed with 2.6 any more (except for single-arg case)
-        //@com.fasterxml.jackson.annotation.JsonCreator
+//        @com.fasterxml.jackson.annotation.JsonCreator
         public XY(int x, int y) {
             this.x = x;
             this.y = y;
@@ -37,14 +37,16 @@
     /**********************************************************
      */
 
-    // for [databind#806]
-    public void testImplicitNameWithNamingStrategy() throws Exception
-    {
-        ObjectMapper mapper = new ObjectMapper()
+    private final ObjectMapper MAPPER = newObjectMapper()
             .setAnnotationIntrospector(new MyParamIntrospector())
             .setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE)
             ;
-        XY value = mapper.readValue(aposToQuotes("{'param_name0':1,'param_name1':2}"), XY.class);
+
+    // for [databind#806]: problem is that renaming occurs too late for implicitly detected
+    // Creators
+    public void testImplicitNameWithNamingStrategy() throws Exception
+    {
+        XY value = MAPPER.readValue(aposToQuotes("{'param_name0':1,'param_name1':2}"), XY.class);
         assertNotNull(value);
         assertEquals(1, value.x);
         assertEquals(2, value.y);
diff --git a/src/test/java/com/fasterxml/jackson/failing/InnerClassNonStaticCore384Test.java b/src/test/java/com/fasterxml/jackson/failing/InnerClassNonStaticCore384Test.java
new file mode 100644
index 0000000..0ef5e76
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/InnerClassNonStaticCore384Test.java
@@ -0,0 +1,209 @@
+package com.fasterxml.jackson.failing;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+
+import static org.junit.Assert.assertThat;
+
+import com.fasterxml.jackson.databind.*;
+
+// see [https://github.com/FasterXML/jackson-core/issues/384]: most likely
+// can not be fixed, but could we improve error message to indicate issue
+// with non-static type of `Car` and `Truck`, which prevent instantiation?
+public class InnerClassNonStaticCore384Test extends BaseMapTest
+{
+    static class Fleet {
+        private List<Vehicle> vehicles;
+
+        public List<Vehicle> getVehicles() {
+            return vehicles;
+        }
+
+        public void setVehicles(List<Vehicle> vehicles) {
+            this.vehicles = vehicles;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            Fleet fleet = (Fleet) o;
+            return Objects.equals(vehicles, fleet.vehicles);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(vehicles);
+        }
+    }
+
+    static abstract class Vehicle {
+        private String make;
+        private String model;
+
+        protected Vehicle(String make, String model) {
+            this.make = make;
+            this.model = model;
+        }
+
+        public Vehicle() {
+        }
+
+        public String getMake() {
+            return make;
+        }
+
+        public void setMake(String make) {
+            this.make = make;
+        }
+
+        public String getModel() {
+            return model;
+        }
+
+        public void setModel(String model) {
+            this.model = model;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof Vehicle)) return false;
+            Vehicle vehicle = (Vehicle) o;
+            return Objects.equals(make, vehicle.make) &&
+                    Objects.equals(model, vehicle.model);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(make, model);
+        }
+    }
+
+    class Car extends Vehicle {
+        private int seatingCapacity;
+        private double topSpeed;
+
+        public Car(String make, String model, int seatingCapacity, double topSpeed) {
+            super(make, model);
+            this.seatingCapacity = seatingCapacity;
+            this.topSpeed = topSpeed;
+        }
+
+        public Car() {
+        }
+
+        public int getSeatingCapacity() {
+            return seatingCapacity;
+        }
+
+        public void setSeatingCapacity(int seatingCapacity) {
+            this.seatingCapacity = seatingCapacity;
+        }
+
+        public double getTopSpeed() {
+            return topSpeed;
+        }
+
+        public void setTopSpeed(double topSpeed) {
+            this.topSpeed = topSpeed;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            if (!super.equals(o)) return false;
+            Car car = (Car) o;
+            return seatingCapacity == car.seatingCapacity &&
+                    Double.compare(car.topSpeed, topSpeed) == 0;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(super.hashCode(), seatingCapacity, topSpeed);
+        }
+    }
+
+    class Truck extends Vehicle {
+        private double payloadCapacity;
+
+        public Truck(String make, String model, double payloadCapacity) {
+            super(make, model);
+            this.payloadCapacity = payloadCapacity;
+        }
+
+        public Truck() {
+        }
+
+        public double getPayloadCapacity() {
+            return payloadCapacity;
+        }
+
+        public void setPayloadCapacity(double payloadCapacity) {
+            this.payloadCapacity = payloadCapacity;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            if (!super.equals(o)) return false;
+            Truck truck = (Truck) o;
+            return Double.compare(truck.payloadCapacity, payloadCapacity) == 0;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(super.hashCode(), payloadCapacity);
+        }
+    }
+
+    /*
+    /**********************************************************************
+    /* Test methods
+    /**********************************************************************
+     */
+
+    public void testHierarchy() throws IOException {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.enableDefaultTyping();
+
+        Fleet fleet = initVehicle();
+
+        /*
+for (Vehicle v : fleet.vehicles) {
+    System.out.println("Vehicle, type: "+v.getClass());
+}
+*/
+        String serializedFleet = mapper
+                .writerWithDefaultPrettyPrinter()
+                .writeValueAsString(fleet);
+
+//System.out.println(serializedFleet);
+
+        Fleet deserializedFleet = mapper.readValue(serializedFleet, Fleet.class);
+
+        assertThat(deserializedFleet.getVehicles().get(0), instanceOf(Car.class));
+        assertThat(deserializedFleet.getVehicles().get(1), instanceOf(Truck.class));
+
+        assertEquals(fleet, deserializedFleet);
+    }
+
+    private Fleet initVehicle() {
+        Car car = new Car("Mercedes-Benz", "S500", 5, 250.0);
+        Truck truck = new Truck("Isuzu", "NQR", 7500.0);
+
+        List<Vehicle> vehicles = new ArrayList<>();
+        vehicles.add(car);
+        vehicles.add(truck);
+
+        Fleet serializedFleet = new Fleet();
+        serializedFleet.setVehicles(vehicles);
+        return serializedFleet;
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/failing/KevinFail1410Test.java b/src/test/java/com/fasterxml/jackson/failing/KevinFail1410Test.java
new file mode 100644
index 0000000..8cba59c
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/KevinFail1410Test.java
@@ -0,0 +1,79 @@
+package com.fasterxml.jackson.failing;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+
+public class KevinFail1410Test extends BaseMapTest
+{
+    enum EnvironmentEventSource { BACKEND; }
+    
+    @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="source")
+    @JsonSubTypes({
+            @JsonSubTypes.Type(value = BackendEvent.class, name = "BACKEND")
+    })
+    static abstract class EnvironmentEvent {
+        private String environmentName;
+        private String message;
+
+        protected EnvironmentEvent() { } // for deserializer
+        protected EnvironmentEvent(String env, String msg) {
+            environmentName = env;
+            message = msg;
+        }
+        public String getEnvironmentName() { return environmentName; }
+        public abstract EnvironmentEventSource getSource();
+        public String getMessage() { return message; }
+    }
+
+    static class BackendEvent extends EnvironmentEvent {
+        private String status;
+
+        private Object resultData;
+
+        protected BackendEvent() {} // for deserializer
+
+        public BackendEvent(String envName, String message, String status, Object results)
+        {
+            super(envName, message);
+            this.status = status;
+            resultData = results;
+        }
+
+        public static BackendEvent create(String environmentName, String message,
+                String status, Object results)
+        {
+            return new BackendEvent(environmentName, message,
+                    status, results);
+        }
+
+        @Override
+        public EnvironmentEventSource getSource() {
+            return EnvironmentEventSource.BACKEND;
+        }
+
+        public String getStatus() {
+            return status;
+        }
+
+        public Object getResultData() {
+            return resultData;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("(%s): %s", status, getMessage());
+        }
+    }
+
+    public void testDupProps() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        EnvironmentEvent event = new BackendEvent("foo", "hello", "bar", null);
+        String ser = mapper
+                .writerWithDefaultPrettyPrinter()
+                .writeValueAsString(event);
+        mapper.readValue(ser, EnvironmentEvent.class);
+        assertNotNull(ser);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/failing/MapEntryFormat1419Test.java b/src/test/java/com/fasterxml/jackson/failing/MapEntryFormat1419Test.java
new file mode 100644
index 0000000..18a4616
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/MapEntryFormat1419Test.java
@@ -0,0 +1,40 @@
+package com.fasterxml.jackson.failing;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.databind.*;
+
+// for [databind#1419]
+public class MapEntryFormat1419Test extends BaseMapTest
+{
+    static class BeanWithMapEntryAsObject {
+        @JsonFormat(shape=JsonFormat.Shape.OBJECT)
+        public Map.Entry<String,String> entry;
+
+        protected BeanWithMapEntryAsObject() { }
+        public BeanWithMapEntryAsObject(String key, String value) {
+            Map<String,String> map = new HashMap<>();
+            map.put(key, value);
+            entry = map.entrySet().iterator().next();
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+
+    public void testWrappedAsObjectRoundtrip() throws Exception
+    {
+        BeanWithMapEntryAsObject input = new BeanWithMapEntryAsObject("foo" ,"bar");
+        String json = MAPPER.writeValueAsString(input);
+        assertEquals(aposToQuotes("{'entry':{'key':'foo','value':'bar'}}"), json);
+        BeanWithMapEntryAsObject result = MAPPER.readValue(json, BeanWithMapEntryAsObject.class);
+        assertEquals("foo", result.entry.getKey());
+        assertEquals("bar", result.entry.getValue());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/failing/MapInclusion1649Test.java b/src/test/java/com/fasterxml/jackson/failing/MapInclusion1649Test.java
new file mode 100644
index 0000000..d2d0906
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/MapInclusion1649Test.java
@@ -0,0 +1,41 @@
+package com.fasterxml.jackson.failing;
+
+import java.io.IOException;
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.*;
+
+public class MapInclusion1649Test extends BaseMapTest
+{
+    @JsonInclude(value=JsonInclude.Include.NON_EMPTY, content=JsonInclude.Include.NON_EMPTY)
+    static class Bean1649 {
+        public Map<String, String> map;
+
+        public Bean1649(String key, String value) {
+            map = new LinkedHashMap<>();
+            map.put(key, value);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    final private ObjectMapper MAPPER = objectMapper();
+
+    // [databind#1649]
+    public void testNonEmptyViaClass() throws IOException
+    {
+        // non-empty/null, include
+        assertEquals(aposToQuotes("{'map':{'a':'b'}}"),
+                MAPPER.writeValueAsString(new Bean1649("a", "b")));
+        // null, empty, nope
+        assertEquals(aposToQuotes("{}"),
+                MAPPER.writeValueAsString(new Bean1649("a", null)));
+        assertEquals(aposToQuotes("{}"),
+                MAPPER.writeValueAsString(new Bean1649("a", "")));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/failing/NoTypeInfo1654Test.java b/src/test/java/com/fasterxml/jackson/failing/NoTypeInfo1654Test.java
new file mode 100644
index 0000000..b39d44a
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/NoTypeInfo1654Test.java
@@ -0,0 +1,75 @@
+package com.fasterxml.jackson.failing;
+
+import java.io.IOException;
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+
+public class NoTypeInfo1654Test extends BaseMapTest
+{
+    // [databind#1654]
+
+    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME)
+    static class Value1654 {
+        public int x;
+
+        protected Value1654() { }
+        public Value1654(int x) { this.x = x; }
+    }
+
+    static class Value1654TypedContainer {
+        public List<Value1654> values;
+
+        protected Value1654TypedContainer() { }
+        public Value1654TypedContainer(Value1654... v) {
+            values = Arrays.asList(v);
+        }
+    }
+
+    static class Value1654UntypedContainer {
+        @JsonDeserialize(contentUsing = Value1654Deserializer.class)
+        @JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
+        public List<Value1654> values;
+    }
+
+    static class Value1654Deserializer extends JsonDeserializer<Value1654> {
+        @Override
+        public Value1654 deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
+            p.skipChildren();
+            return new Value1654(13);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    // [databind#1654]
+    public void testNoTypeElementOverride() throws Exception
+    {
+        final ObjectMapper mapper = newObjectMapper();
+
+        // First: regular typed case
+        String json = mapper.writeValueAsString(new Value1654TypedContainer(
+                new Value1654(1),
+                new Value1654(2),
+                new Value1654(3)
+        ));
+        Value1654TypedContainer result = mapper.readValue(json, Value1654TypedContainer.class);
+        assertEquals(3, result.values.size());
+        assertEquals(2, result.values.get(1).x);
+
+        // and then actual failing case
+        final String noTypeJson = aposToQuotes(
+                "{'values':[{'x':3},{'x': 7}] }"
+                );
+        Value1654UntypedContainer unResult = mapper.readValue(noTypeJson, Value1654UntypedContainer.class);
+        assertEquals(2, unResult.values.size());
+        assertEquals(7, unResult.values.get(1).x);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/failing/NodeContext2049Test.java b/src/test/java/com/fasterxml/jackson/failing/NodeContext2049Test.java
new file mode 100644
index 0000000..ab198d4
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/NodeContext2049Test.java
@@ -0,0 +1,183 @@
+package com.fasterxml.jackson.failing;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.*;
+import com.fasterxml.jackson.databind.deser.std.CollectionDeserializer;
+import com.fasterxml.jackson.databind.deser.std.DelegatingDeserializer;
+import com.fasterxml.jackson.databind.type.CollectionLikeType;
+
+public class NodeContext2049Test extends BaseMapTest
+{
+    public interface HasParent {
+        void setParent(Parent parent);
+        Parent getParent();
+    }
+
+    static class Child implements HasParent {
+        public Parent parent;
+        public String property;
+
+        @Override
+        public void setParent(Parent p) { parent = p; }
+        @Override
+        public Parent getParent() { return parent; }
+    }
+
+    static class Parent {
+        public List<Child> children;
+        public Child singleChild;
+    }
+
+    static class ListValueInstantiator extends ValueInstantiator {
+        @Override
+        public String getValueTypeDesc() {
+             return List.class.getName();
+        }
+
+        @Override
+        public Object createUsingDefault(DeserializationContext ctxt) throws IOException {
+             return new ArrayList<>();
+        }
+    }
+
+    static class ParentSettingDeserializerModifier extends BeanDeserializerModifier {
+        @Override
+        public BeanDeserializerBuilder updateBuilder(DeserializationConfig config, BeanDescription beanDesc,
+                  BeanDeserializerBuilder builder) {
+             for (Iterator<SettableBeanProperty> propertyIt = builder.getProperties(); propertyIt.hasNext(); ) {
+                  SettableBeanProperty property = propertyIt.next();
+                  builder.addOrReplaceProperty(property.withValueDeserializer(new ParentSettingDeserializerContextual()), false);
+             }
+             return builder;
+        }
+    }
+
+    @SuppressWarnings("serial")
+    static class ParentSettingDeserializer extends DelegatingDeserializer {
+        public ParentSettingDeserializer(JsonDeserializer<?> delegatee) {
+             super(delegatee);
+        }
+
+        @Override
+        public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
+             Object retValue = super.deserialize(jp, ctxt);
+             if (retValue instanceof HasParent) {
+                  HasParent obj = (HasParent) retValue;
+                  Parent parent = null;
+                  JsonStreamContext parsingContext = jp.getParsingContext();
+                  while (parent == null && parsingContext != null) {
+                       Object currentValue = parsingContext.getCurrentValue();
+                       if (currentValue != null && currentValue instanceof Parent) {
+                            parent = (Parent) currentValue;
+                       }
+                       parsingContext = parsingContext.getParent();
+                  }
+                  if (parent != null) {
+                       obj.setParent(parent);
+                  }
+             }
+             return retValue;
+        }
+
+        @Override
+        protected JsonDeserializer<?> newDelegatingInstance(JsonDeserializer<?> newDelegatee) {
+             return new ParentSettingDeserializer(newDelegatee);
+        }
+
+   }
+    
+    static class ParentSettingDeserializerContextual extends JsonDeserializer<Object> implements ContextualDeserializer {
+
+        @Override
+        public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)
+                  throws JsonMappingException {
+             JavaType propertyType = property.getType();
+             JavaType contentType = propertyType;
+             if (propertyType.isCollectionLikeType()) {
+                  contentType = propertyType.getContentType();
+             }
+             JsonDeserializer<Object> delegatee = ctxt.findNonContextualValueDeserializer(contentType);
+             JsonDeserializer<Object> objectDeserializer = new ParentSettingDeserializer(delegatee);
+             JsonDeserializer<?> retValue;
+             if (propertyType.isCollectionLikeType()) {
+                  CollectionLikeType collectionType = ctxt.getTypeFactory().constructCollectionLikeType(propertyType.getRawClass(),
+                            contentType);
+                  ValueInstantiator instantiator = new ListValueInstantiator();
+                  retValue = new CollectionDeserializer(collectionType, objectDeserializer, null, instantiator);
+             } else {
+                  retValue = objectDeserializer;
+             }
+             return retValue;
+        }
+
+        @Override
+        public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
+             // TODO Auto-generated method stub
+             return null;
+        }
+
+   }
+    
+    /*
+    /**********************************************************************
+    /* Test methods
+    /**********************************************************************
+     */
+    
+    private ObjectMapper objectMapper;
+    {
+        objectMapper = new ObjectMapper();
+        objectMapper.registerModule(new Module() {
+              @Override
+              public String getModuleName() {
+                   return "parentSetting";
+              }
+              @Override
+              public Version version() {
+                   return Version.unknownVersion();
+              }
+              @Override
+              public void setupModule(SetupContext context) {
+                   context.addBeanDeserializerModifier(new ParentSettingDeserializerModifier());
+              }
+         });
+    }
+
+    final static String JSON = "{\n" + 
+            "     \"children\": [\n" + 
+            "          {\n" + 
+            "               \"property\": \"value1\"\n" + 
+            "          },\n" + 
+            "          {\n" + 
+            "               \"property\": \"value2\"\n" + 
+            "          }\n" + 
+            "     ],\n" + 
+            "     \"singleChild\": {\n" + 
+            "          \"property\": \"value3\"\n" + 
+            "     }\n" + 
+            "}";
+
+    public void testReadNoBuffering() throws IOException {
+        Parent obj = objectMapper.readerFor(Parent.class).readValue(JSON);
+        assertSame(obj, obj.singleChild.getParent());
+        for (Child child : obj.children) {
+            assertSame(obj, child.getParent());
+        }
+    }
+
+    public void testReadFromTree() throws IOException {
+        JsonNode tree = objectMapper.readTree(JSON);
+        Parent obj = objectMapper.reader().forType(Parent.class).readValue(tree);
+        assertSame(obj, obj.singleChild.getParent());
+        for (Child child : obj.children) {
+            assertSame(obj, child.getParent());
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/failing/NullConversionWithCreatorTest.java b/src/test/java/com/fasterxml/jackson/failing/NullConversionWithCreatorTest.java
new file mode 100644
index 0000000..39e4043
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/NullConversionWithCreatorTest.java
@@ -0,0 +1,62 @@
+package com.fasterxml.jackson.failing;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.exc.InvalidNullException;
+
+public class NullConversionWithCreatorTest extends BaseMapTest
+{
+    // [databind#2024]
+    static class EmptyFromNullViaCreator {
+        @JsonSetter(nulls=Nulls.AS_EMPTY)
+        Point p;
+
+        @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
+        public EmptyFromNullViaCreator(@JsonSetter(nulls=Nulls.AS_EMPTY)
+            @JsonProperty("p") Point p)
+        {
+            this.p = p;
+        }
+    }
+
+    static class FailFromNullViaCreator {
+        @JsonSetter(nulls=Nulls.AS_EMPTY)
+        Point p;
+
+        @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
+        public FailFromNullViaCreator(@JsonSetter(nulls=Nulls.FAIL)
+            @JsonProperty("p") Point p)
+        {
+            this.p = p;
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+    private final ObjectMapper MAPPER = newObjectMapper();
+
+    // [databind#2024]
+    public void testEmptyFromNullViaCreator() throws Exception
+    {
+        EmptyFromNullViaCreator result = MAPPER.readValue(aposToQuotes("{'p':null}"),
+                EmptyFromNullViaCreator.class);
+        assertNotNull(result);
+        assertNotNull(result.p);
+    }
+
+    // [databind#2024]
+    public void testFailForNullViaCreator() throws Exception
+    {
+        try {
+            /*FailFromNullViaCreator result =*/ MAPPER.readValue(aposToQuotes("{'p':null}"),
+                    FailFromNullViaCreator.class);
+            fail("Should not pass");
+        } catch (InvalidNullException e) {
+            verifyException(e, "property \"p\"");
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/failing/NumberNodes1770Test.java b/src/test/java/com/fasterxml/jackson/failing/NumberNodes1770Test.java
new file mode 100644
index 0000000..93e3602
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/NumberNodes1770Test.java
@@ -0,0 +1,23 @@
+package com.fasterxml.jackson.failing;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Basic tests for {@link JsonNode} implementations that
+ * contain numeric values.
+ */
+public class NumberNodes1770Test extends BaseMapTest
+{
+    private final ObjectMapper MAPPER = newObjectMapper();
+
+    // Related to [databind#1770]
+    public void testBigDecimalCoercion() throws Exception
+    {
+        final JsonNode jsonNode = MAPPER.reader()
+            .with(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)
+            .readTree("7976931348623157e309");
+        assertTrue(jsonNode.isBigDecimal());
+        // the following fails with NumberFormatException, because jsonNode is a DoubleNode with a value of POSITIVE_INFINITY
+//        Assert.assertTrue(jsonNode.decimalValue().compareTo(new BigDecimal("7976931348623157e309")) == 0);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/failing/ObjectIdWithBuilder1496Test.java b/src/test/java/com/fasterxml/jackson/failing/ObjectIdWithBuilder1496Test.java
new file mode 100644
index 0000000..8338ed2
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/ObjectIdWithBuilder1496Test.java
@@ -0,0 +1,69 @@
+package com.fasterxml.jackson.failing;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+
+public class ObjectIdWithBuilder1496Test extends BaseMapTest
+{
+    @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
+    @JsonDeserialize(builder=POJOBuilder.class)
+    static class POJO
+    {
+         private long id;
+         public long getId() { return id; }
+         private int var;
+         public int getVar() { return var; }
+         private POJO (long id, int var) { this.id = id; this.var = var; }
+
+         @Override
+         public String toString() { return "id: " + id + ", var: " + var; }
+    }
+         
+    @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
+    @JsonPOJOBuilder(withPrefix = "", buildMethodName="readFromCacheOrBuild")
+    static final class POJOBuilder {
+        // Standard builder stuff
+        private long id;
+        private int var;
+
+        public POJOBuilder id(long _id) { id = _id; return this; }
+        public POJOBuilder var(int _var) { var = _var; return this; }
+
+        public POJO build() { return new POJO(id, var); }
+        
+        // Special build method for jackson deserializer that caches objects already deserialized
+        private final static ConcurrentHashMap<Long, POJO> cache = new ConcurrentHashMap<>();
+        public POJO readFromCacheOrBuild() {
+            POJO pojo = cache.get(id);
+            if (pojo == null) {
+                POJO newPojo = build();
+                pojo = cache.putIfAbsent(id, newPojo);
+                if (pojo == null) {
+                    pojo = newPojo;
+                }
+            }
+            return pojo;
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = newObjectMapper();
+    
+    public void testBuilderId1496() throws Exception
+    {
+        POJO input = new POJOBuilder().id(123L).var(456).build();
+        String json = MAPPER.writeValueAsString(input);
+        POJO result = MAPPER.readValue(json, POJO.class);
+        assertNotNull(result);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/failing/PolymorphicWithObjectId1551Test.java b/src/test/java/com/fasterxml/jackson/failing/PolymorphicWithObjectId1551Test.java
deleted file mode 100644
index 6944644..0000000
--- a/src/test/java/com/fasterxml/jackson/failing/PolymorphicWithObjectId1551Test.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package com.fasterxml.jackson.failing;
-
-import com.fasterxml.jackson.annotation.JsonIdentityInfo;
-import com.fasterxml.jackson.annotation.JsonIdentityReference;
-import com.fasterxml.jackson.annotation.JsonTypeInfo;
-import com.fasterxml.jackson.annotation.ObjectIdGenerators;
-import com.fasterxml.jackson.databind.*;
-
-public class PolymorphicWithObjectId1551Test extends BaseMapTest
-{
-    @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY,
-            property = "@class")
-    static abstract class Vehicle {
-        public String vehicleId;
-    }
-
-    static class Car extends Vehicle {
-        public int numberOfDoors;
-    }
-
-    static class VehicleOwnerViaProp {
-        @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "vehicleId")
-        @JsonIdentityReference(alwaysAsId = false)
-        public Vehicle ownedVehicle;
-    }
-
-    public void testWithAbstractUsingProp() throws Exception {
-        Car c = new Car();
-        c.vehicleId = "123";
-        c.numberOfDoors = 2;
-        // both owners own the same vehicle (car sharing ;-))
-        VehicleOwnerViaProp v1 = new VehicleOwnerViaProp();
-        v1.ownedVehicle = c;
-        VehicleOwnerViaProp v2 = new VehicleOwnerViaProp();
-        v2.ownedVehicle = c;
-
-        ObjectMapper objectMapper = new ObjectMapper();
-        String serialized = objectMapper.writer()
-                .writeValueAsString(new VehicleOwnerViaProp[] { v1, v2 });
-
-        // 02-May-2017, tatu: Not possible to support as of Jackson 2.8 at least, so:
-
-        try {
-            /*VehicleOwnerViaProp[] deserialized = */
-            objectMapper.readValue(serialized, VehicleOwnerViaProp[].class);
-            fail("Should not pass");
-        } catch (JsonMappingException e) {
-            verifyException(e, "Invalid Object Id definition for abstract type");
-        }
-//        assertEquals(2, deserialized.length);
-//        assertSame(deserialized[0].ownedVehicle, deserialized[1].ownedVehicle);
-    }
-}
diff --git a/src/test/java/com/fasterxml/jackson/failing/RecursiveIgnoreProperties1755Test.java b/src/test/java/com/fasterxml/jackson/failing/RecursiveIgnoreProperties1755Test.java
new file mode 100644
index 0000000..9090429
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/RecursiveIgnoreProperties1755Test.java
@@ -0,0 +1,65 @@
+package com.fasterxml.jackson.failing;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+
+import com.fasterxml.jackson.databind.*;
+
+public class RecursiveIgnoreProperties1755Test extends BaseMapTest
+{
+    // for [databind#1755]
+    static class JackBase1755 {
+        public String id;
+    }
+
+    static class JackExt extends JackBase1755 {
+        public BigDecimal quantity;
+        public String ignoreMe;
+
+        @JsonIgnoreProperties({"ignoreMe"})
+        public List<JackExt> linked;
+
+        public List<KeyValue> metadata;
+    }
+
+    static class KeyValue {
+        public String key;
+        public String value;
+    }
+
+    // for [databind#1755]
+
+    private final ObjectMapper MAPPER = newObjectMapper();
+
+    public void testRecursiveIgnore1755() throws Exception
+    {
+        final String JSON = aposToQuotes("{\n"
+                +"'id': '1',\n"
+                +"'quantity': 5,\n"
+                +"'ignoreMe': 'yzx',\n"
+                +"'metadata': [\n"
+                +"           {\n"
+                +"              'key': 'position',\n"
+                +"              'value': '2'\n"
+                +"          }\n"
+                +"       ],\n"
+                +"'linked': [\n"
+                +"     {\n"
+                +"         'id': '1',\n"
+                +"         'quantity': 5,\n"
+                +"         'ignoreMe': 'yzx',\n"
+                +"         'metadata': [\n"
+                +"          {\n"
+                +"              'key': 'position',\n"
+                +"             'value': '2'\n"
+                +"         }\n"
+                +"     ]\n"
+                +"   }\n"
+                +"  ]\n"
+                +"}");
+        JackExt value = MAPPER.readValue(JSON, JackExt.class);
+        assertNotNull(value);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/failing/SkipInjectableIntrospection962Test.java b/src/test/java/com/fasterxml/jackson/failing/SkipInjectableIntrospection962Test.java
index cb37a5c..88f283a 100644
--- a/src/test/java/com/fasterxml/jackson/failing/SkipInjectableIntrospection962Test.java
+++ b/src/test/java/com/fasterxml/jackson/failing/SkipInjectableIntrospection962Test.java
@@ -3,6 +3,7 @@
 import com.fasterxml.jackson.annotation.JacksonInject;
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.OptBoolean;
 import com.fasterxml.jackson.databind.*;
 
 public class SkipInjectableIntrospection962Test extends BaseMapTest
@@ -30,8 +31,10 @@
     {
         private String b;
 
+        // Important! Prevent binding from data
         @JsonCreator
-        public Injectee(@JacksonInject InjectMe injectMe, @JsonProperty("b") String b) {
+        public Injectee(@JacksonInject(useInput=OptBoolean.FALSE) InjectMe injectMe,
+                @JsonProperty("b") String b) {
             this.b = b;
         }
 
diff --git a/src/test/java/com/fasterxml/jackson/failing/StaticTyping1515Test.java b/src/test/java/com/fasterxml/jackson/failing/StaticTyping1515Test.java
new file mode 100644
index 0000000..7a1a554
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/StaticTyping1515Test.java
@@ -0,0 +1,77 @@
+package com.fasterxml.jackson.failing;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+public class StaticTyping1515Test extends BaseMapTest
+{
+    static abstract class Base {
+        public int a = 1;
+    }
+
+    static class Derived extends Base {
+        public int b = 2;
+    }
+
+    @JsonSerialize(typing = JsonSerialize.Typing.DYNAMIC)
+    static abstract class BaseDynamic {
+        public int a = 3;
+    }
+
+    static class DerivedDynamic extends BaseDynamic {
+        public int b = 4;
+    }
+
+    @JsonPropertyOrder({ "value", "aValue", "dValue" })
+    static class Issue515Singles {
+        public Base value = new Derived();
+
+        @JsonSerialize(typing = JsonSerialize.Typing.DYNAMIC)
+        public Base aValue = new Derived();
+
+        public BaseDynamic dValue = new DerivedDynamic();
+    }
+
+    @JsonPropertyOrder({ "list", "aList", "dList" })
+    static class Issue515Lists {
+        public List<Base> list = new ArrayList<>(); {
+            list.add(new Derived());
+        }
+
+        @JsonSerialize(typing = JsonSerialize.Typing.DYNAMIC)
+        public List<Base> aList = new ArrayList<>(); {
+            aList.add(new Derived());
+        }
+
+        public List<BaseDynamic> dList = new ArrayList<>(); {
+            dList.add(new DerivedDynamic());
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    private final ObjectMapper STAT_MAPPER = newObjectMapper();
+    {
+        STAT_MAPPER.enable(MapperFeature.USE_STATIC_TYPING);
+    }
+
+    public void testStaticTypingForProperties() throws Exception
+    {
+        String json = STAT_MAPPER.writeValueAsString(new Issue515Singles());
+        assertEquals(aposToQuotes("{'value':{'a':1},'aValue':{'a':1,'b':2},'dValue':{'a':3,'b':4}}"), json);
+    }
+
+    public void testStaticTypingForLists() throws Exception
+    {
+        String json = STAT_MAPPER.writeValueAsString(new Issue515Lists());
+        assertEquals(aposToQuotes("{'list':[{'a':1}],'aList':[{'a':1,'b':2}],'dList:[{'a':3,'b':4}]}"), json);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestConvertingSerializer357.java b/src/test/java/com/fasterxml/jackson/failing/TestConvertingSerializer357.java
deleted file mode 100644
index ef0d30e..0000000
--- a/src/test/java/com/fasterxml/jackson/failing/TestConvertingSerializer357.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package com.fasterxml.jackson.failing;
-
-import java.util.*;
-
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-
-import com.fasterxml.jackson.databind.util.StdConverter;
-
-public class TestConvertingSerializer357
-    extends com.fasterxml.jackson.databind.BaseMapTest
-{
-    // [databind#357]
-    static class Value { }
-
-    static class ListWrapper {
-        @JsonSerialize(contentConverter = ValueToStringListConverter.class)
-        public List<Value> list = Arrays.asList(new Value());
-    }
-
-    static class ValueToStringListConverter extends StdConverter<Value, List<String>> {
-        @Override
-        public List<String> convert(Value value) {
-            return Arrays.asList("Hello world!");
-        }
-    }
-
-    /*
-    /**********************************************************
-    /* Test methods
-    /**********************************************************
-     */
-
-    // [databind#357]
-    public void testConverterForList357() throws Exception {
-        String json = objectWriter().writeValueAsString(new ListWrapper());
-        assertEquals("{\"list\":[[\"Hello world!\"]]}", json);
-    }
-}
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestMultipleExternalIds291.java b/src/test/java/com/fasterxml/jackson/failing/TestMultipleExternalIds291.java
deleted file mode 100644
index 75ef0c2..0000000
--- a/src/test/java/com/fasterxml/jackson/failing/TestMultipleExternalIds291.java
+++ /dev/null
@@ -1,71 +0,0 @@
-package com.fasterxml.jackson.failing;
-
-import com.fasterxml.jackson.annotation.*;
-import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
-import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
-import com.fasterxml.jackson.databind.*;
-
-public class TestMultipleExternalIds291 extends BaseMapTest
-{
-    // For [Issue#291]
-    interface F1 {}
-
-    static class A implements F1 {
-        public String a;
-    }
-
-    static class B implements F1 {
-        public String b;
-    }
-
-    static interface F2 {}
-
-    static class C implements F2 {
-        public String c;
-    }
-
-    static class D implements F2{
-        public String d;
-    }
-
-    static class Container {
-        public String type;
-
-        @JsonTypeInfo(use = Id.NAME, property = "type", include = As.EXTERNAL_PROPERTY)
-        @JsonSubTypes({
-                @JsonSubTypes.Type(value = A.class, name = "1"),
-                @JsonSubTypes.Type(value = B.class, name = "2")})
-        public F1 field1;
-
-        @JsonTypeInfo(use = Id.NAME, property = "type", include = As.EXTERNAL_PROPERTY)
-        @JsonSubTypes({
-                @JsonSubTypes.Type(value = C.class, name = "1"),
-                @JsonSubTypes.Type(value = D.class, name = "2")})
-        public F2 field2;
-    }
-
-    /*
-    /**********************************************************
-    /* Test methods
-    /**********************************************************
-     */
-
-    // [databind#291]
-    public void testMultiple() throws Exception
-    {
-        final ObjectMapper mapper = objectMapper();
-        final String JSON =
-"{\"type\" : \"1\",\n"
-+"\"field1\" : {\n"
-+"  \"a\" : \"AAA\"\n"
-+"}, \"field2\" : {\n"
-+"  \"c\" : \"CCC\"\n"
-+"}\n"
-+"}";
-
-        Container c = mapper.readValue(JSON, Container.class);
-        assertNotNull(c);
-        assertTrue(c.field1 instanceof A);
-        assertTrue(c.field2 instanceof C);
-    }
-}
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestObjectIdWithUnwrapping1298.java b/src/test/java/com/fasterxml/jackson/failing/TestObjectIdWithUnwrapping1298.java
index 8c4b97b..d039140 100644
--- a/src/test/java/com/fasterxml/jackson/failing/TestObjectIdWithUnwrapping1298.java
+++ b/src/test/java/com/fasterxml/jackson/failing/TestObjectIdWithUnwrapping1298.java
@@ -45,6 +45,7 @@
     public void testObjectIdWithRepeatedChild() throws Exception
     {
         ObjectMapper mapper = new ObjectMapper();
+        // to keep output faithful to original, prevent auto-closing...
         mapper.disable(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT);
 
         // Equivalent to Spring _embedded for Bean w/ List property
@@ -58,9 +59,10 @@
 
         // serialize parent1 and parent2
         String json = mapper
-//                .writerWithDefaultPrettyPrinter()
+                .writerWithDefaultPrettyPrinter()
                 .writeValueAsString(parents);
-        System.out.println("This works: " + json);
+        assertNotNull(json);
+//        System.out.println("This works: " + json);
 
         // Add parent3 to create ObjectId reference
         // Bean w/ repeated relationship from parent1, should generate ObjectId
@@ -74,10 +76,7 @@
 //                .writerWithDefaultPrettyPrinter()
                 .writeValue(sw, parents);
         } catch (Exception e) {
-            System.out.println("Failed output so far: " + sw);
-            throw e;
+            fail("Failed with "+e.getClass().getName()+", output so far: " + sw);
         }
-
-        System.out.println("Also works: " + sw);
     }
 }
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestPolymorphicDeserialization283.java b/src/test/java/com/fasterxml/jackson/failing/TestPolymorphicDeserialization283.java
deleted file mode 100644
index bd1c254..0000000
--- a/src/test/java/com/fasterxml/jackson/failing/TestPolymorphicDeserialization283.java
+++ /dev/null
@@ -1,73 +0,0 @@
-package com.fasterxml.jackson.failing;
-
-import com.fasterxml.jackson.annotation.JsonSubTypes;
-import com.fasterxml.jackson.annotation.JsonTypeInfo;
-import com.fasterxml.jackson.databind.BaseMapTest;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.junit.Assert;
-import org.junit.Test;
-
-/**
- * Reproduction of [https://github.com/FasterXML/jackson-databind/issues/283],
- * contributed by Eric T.
- *<p>
- * Problem here is that although explicit concrete class is indicated, polymorphic
- * deserializer comes to different conclusion (using default implementation class),
- * resulting in a <code>ClassCastException</code>.
- * Whether this is wrong, and if so, can we fix it, is unknown at this point
- * (2.6): quite possibly this can not be changed.
- */
-public class TestPolymorphicDeserialization283 extends BaseMapTest
-{
-    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = ClassA.class)
-    @JsonSubTypes({
-            @JsonSubTypes.Type(name = "a", value = ClassA.class),
-            @JsonSubTypes.Type(name = "b", value = ClassB.class)
-    })
-    public static interface SomeInterface
-    {
-      public String get();
-    }
-
-    public static class ClassA implements SomeInterface
-    {
-      @Override
-      public String get()
-      {
-        return "A";
-      }
-    }
-
-    public static class ClassB implements SomeInterface
-    {
-      @Override
-      public String get()
-      {
-        return "B";
-      }
-    }
-
-    public static class ClassC implements SomeInterface
-    {
-      @Override
-      public String get()
-      {
-        return "C";
-      }
-    }
-
-    @Test
-    public void testName() throws Exception
-    {
-      ObjectMapper mapper = objectMapper();
-
-      Assert.assertEquals("A", mapper.readValue("{\"type\": \"a\"}", SomeInterface.class).get());
-      Assert.assertEquals("A", mapper.readValue("{}", SomeInterface.class).get());
-      Assert.assertEquals("B", mapper.readValue("{\"type\": \"b\"}", SomeInterface.class).get());
-      Assert.assertEquals("A", mapper.readValue("{\"type\": \"c\"}", SomeInterface.class).get());
-
-      Assert.assertEquals("A", mapper.readValue("{\"type\": \"a\"}", ClassA.class).get());
-      Assert.assertEquals("B", mapper.readValue("{\"type\": \"b\"}", ClassB.class).get());
-      Assert.assertEquals("C", mapper.readValue("{\"type\": \"c\"}", ClassC.class).get());
-    }
-}
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestSubtypes1311.java b/src/test/java/com/fasterxml/jackson/failing/TestSubtypes1311.java
deleted file mode 100644
index b3cfbc9..0000000
--- a/src/test/java/com/fasterxml/jackson/failing/TestSubtypes1311.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package com.fasterxml.jackson.failing;
-
-import com.fasterxml.jackson.annotation.JsonTypeInfo;
-import com.fasterxml.jackson.annotation.JsonTypeName;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-// Not sure if this is valid, but for what it's worth, shows
-// the thing wrt [databind#1311]. May be removed if we can't find
-// improvements.
-public class TestSubtypes1311 extends com.fasterxml.jackson.databind.BaseMapTest
-{
-    // [databind#1311]
-    @JsonTypeInfo(property = "type", use = JsonTypeInfo.Id.NAME, defaultImpl = Factory1311ImplA.class)
-    interface Factory1311 { }
-
-    @JsonTypeName("implA")
-    static class Factory1311ImplA implements Factory1311 { }
-
-    @JsonTypeName("implB")
-    static class Factory1311ImplB implements Factory1311 { }
-
-    /*
-    /**********************************************************
-    /* Unit tests
-    /**********************************************************
-     */
-    
-    // [databind#1311]
-    public void testSubtypeAssignmentCheck() throws Exception
-    {
-        ObjectMapper mapper = new ObjectMapper();
-        mapper.registerSubtypes(Factory1311ImplA.class, Factory1311ImplB.class);
-        Factory1311ImplB result = mapper.readValue("{\"type\":\"implB\"}", Factory1311ImplB.class);
-        assertNotNull(result);
-        assertEquals(Factory1311ImplB.class, result.getClass());
-    }
-}
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestUnwrappedWithCreator265.java b/src/test/java/com/fasterxml/jackson/failing/TestUnwrappedWithCreator265.java
deleted file mode 100644
index a65a678..0000000
--- a/src/test/java/com/fasterxml/jackson/failing/TestUnwrappedWithCreator265.java
+++ /dev/null
@@ -1,75 +0,0 @@
-package com.fasterxml.jackson.failing;
-
-import com.fasterxml.jackson.annotation.*;
-
-import com.fasterxml.jackson.databind.*;
-
-public class TestUnwrappedWithCreator265 extends BaseMapTest
-{
-    static class JAddress {
-        protected String address;
-        protected String city;
-        protected String state;
-         
-        @JsonCreator
-        public JAddress( @JsonProperty("address") String address,
-                @JsonProperty("city") String city,
-                @JsonProperty("state") String state
-        ){
-            this.address = address;
-            this.city = city;
-            this.state = state;
-        }
-         
-        public String getAddress1() { return address; }
-        public String getCity() { return city; }
-        public String getState() { return state; }
-    }
-
-    static class JPerson {
-        protected String _name;
-        protected JAddress _address;
-        protected String _alias;
-         
-        @JsonCreator
-        public JPerson(@JsonProperty("name") String name,
-        @JsonUnwrapped JAddress address,
-        @JsonProperty("alias") String alias) {
-            _name = name;
-            _address = address;
-            _alias = alias;
-        }
-         
-        public String getName() {
-            return _name;
-        }
-         
-        @JsonUnwrapped public JAddress getAddress() {
-            return _address;
-        }
-         
-        public String getAlias() { return _alias; }
-    }
-
-    /*
-    /**********************************************************
-    /* Test methods
-    /**********************************************************
-     */
-
-    // For [Issue#265] / [Scala#90]
-    public void testUnwrappedWithCreator() throws Exception
-    {
-        JPerson person = new JPerson("MyName", new JAddress("main street", "springfield", "WA"), "bubba");
-        ObjectMapper mapper = new ObjectMapper();
-        String json = mapper.writeValueAsString(person);
-        JPerson result = mapper.readValue(json, JPerson.class);
-        assertNotNull(result);
-        assertEquals(person._name, result._name);
-        assertNotNull(result._address);
-        assertEquals(person._address.city, result._address.city);
-
-        // and see that round-tripping works
-        assertEquals(json, mapper.writeValueAsString(result));
-    }
-}